Skip to content

UX: show My messages count in sidebar #33539

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Jul 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
0eff067
dev: adds tests for unread messages - take 1
yuriyaran Jul 9, 2025
aea8833
dev: adds count/indicator to sidebar messages link - take 2
yuriyaran Jul 17, 2025
8c006ae
dev: counts all new/unread messages - take 3
yuriyaran Jul 17, 2025
d14cb85
dev: adds count to my messages link - take 4
yuriyaran Jul 20, 2025
a2f31d1
dev: moves pm messages tracking up level to discovery routes
yuriyaran Jul 20, 2025
1c4c15a
dev: reverts removal of pmTopicTrackingState.startTracking() for user…
yuriyaran Jul 21, 2025
d2e8a50
dev: improves sidebar my messages spec
yuriyaran Jul 21, 2025
5af8fa4
dev: moves pm tracking trigger to a generic discourse route
yuriyaran Jul 21, 2025
ff70a88
dev: sets my messages badgeText same for old and new new views
yuriyaran Jul 21, 2025
dfee4aa
fix: track PMs: adds check if logged in user can_send_private_messages
yuriyaran Jul 21, 2025
e83ba41
fix: discourse route: adds missing router service
yuriyaran Jul 21, 2025
47883d1
dev: transfers pm subscription from service to standalone instance in…
yuriyaran Jul 22, 2025
2f921dc
dev: adds pmTopicTrackingState.startTracking call in subscribe-user-n…
yuriyaran Jul 22, 2025
582496e
dev: removes wrong approach
yuriyaran Jul 22, 2025
ef77e86
dev: removes afterModel calls for pmTopicTrackingState.startTracking
yuriyaran Jul 22, 2025
21fb964
dev: adds missing router service invocations
yuriyaran Jul 22, 2025
a6fbb35
dev: fixes page-tracking-test
yuriyaran Jul 22, 2025
cee6aa0
dev: improves test message
yuriyaran Jul 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { service } from "@ember/service";
import DiscourseRoute from "discourse/routes/discourse";

export default class AdminSiteSettingsCategoryRoute extends DiscourseRoute {
@service router;

model(params) {
return this.modelFor("adminSiteSettings").filteredSettings.findBy(
"nameKey",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { i18n } from "discourse-i18n";

export default class extends Controller {
@service currentUser;
@service pmTopicTrackingState;
@service router;
@controller user;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import Notification from "discourse/models/notification";
class SubscribeUserNotificationsInit {
@service currentUser;
@service messageBus;
@service pmTopicTrackingState;
@service store;
@service appEvents;
@service siteSettings;
Expand Down Expand Up @@ -67,6 +68,8 @@ class SubscribeUserNotificationsInit {

this.messageBus.subscribe("/client_settings", this.onClientSettings);

this.pmTopicTrackingState.startTracking();

if (!isTesting()) {
this.messageBus.subscribe(alertChannel(this.currentUser), this.onAlert);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { service } from "@ember/service";
import BaseSectionLink from "discourse/lib/sidebar/base-community-section-link";
import { i18n } from "discourse-i18n";

export default class MyMessagesSectionLink extends BaseSectionLink {
@service pmTopicTrackingState;

get name() {
return "my-messages";
}
Expand All @@ -27,6 +30,63 @@ export default class MyMessagesSectionLink extends BaseSectionLink {
);
}

get totalCount() {
const newUserMsgs = this._lookupCount({ type: "new", inboxFilter: "user" });
const unreadUserMsgs = this._lookupCount({
type: "unread",
inboxFilter: "user",
});
const groupMsgsCount = this.currentUser.groupsWithMessages?.reduce(
(count, group) => {
const newGroupMsgs = this._lookupCount({
type: "new",
inboxFilter: "group",
groupName: group.name,
});
const unreadGroupMsgs = this._lookupCount({
type: "unread",
inboxFilter: "group",
groupName: group.name,
});

return count + newGroupMsgs + unreadGroupMsgs;
},
0
);

return newUserMsgs + unreadUserMsgs + groupMsgsCount;
}

_lookupCount({ type, inboxFilter, groupName }) {
const opts = { inboxFilter };
return this.pmTopicTrackingState.lookupCount(
type,
groupName ? { ...opts, groupName } : opts
);
}

get showCount() {
return this.currentUser?.sidebarShowCountOfNewItems;
}

get badgeText() {
return this.showCount && this.totalCount;
}

get suffixCSSClass() {
return "unread";
}

get suffixType() {
return "icon";
}

get suffixValue() {
if (!this.showCount && this.totalCount > 0) {
return "circle";
}
}

get shouldDisplay() {
return this.currentUser?.can_send_private_messages;
}
Expand Down
2 changes: 1 addition & 1 deletion app/assets/javascripts/discourse/app/routes/discourse.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { service } from "@ember/service";
import { seenUser } from "discourse/lib/user-presence";

export default class DiscourseRoute extends Route {
@service router;
@service currentUser;

willTransition() {
seenUser();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import DiscourseRoute from "discourse/routes/discourse";
export default class TopicFromParams extends DiscourseRoute {
@service composer;
@service header;
@service router;

// Avoid default model hook
model(params) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
import { action } from "@ember/object";
import { service } from "@ember/service";
import DiscourseRoute from "discourse/routes/discourse";

export default class UserPrivateMessages extends DiscourseRoute {
@service composer;

templateName = "user/messages";

afterModel() {
this.pmTopicTrackingState.startTracking();
}

@action
triggerRefresh() {
this.refresh();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ export default RouteTemplate(
{{icon "circle-plus"}}
<span>{{@controller.unreadLinkText}}</span>
</DNavigationItem>

{{/if}}

<DNavigationItem
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ acceptance("Page tracking - loading slider", function (needs) {
assertRequests({
assert,
tracked: 0,
untracked: 0,
message: "no requests before app boot",
untracked: 1,
message: "no tracked requests before app boot",
});

await visit("/");
Expand Down Expand Up @@ -94,8 +94,8 @@ acceptance("Page tracking - loading spinner", function (needs) {
assertRequests({
assert,
tracked: 0,
untracked: 0,
message: "no requests before app boot",
untracked: 1,
message: "no tracked requests before app boot",
});

await visit("/");
Expand Down
28 changes: 19 additions & 9 deletions spec/system/page_objects/components/navigation_menu/sidebar.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,18 +78,28 @@ def my_messages
I18n.t("js.sidebar.sections.community.links.my_messages.content")
end

def my_messages_link_css
".sidebar-section-link[data-link-name='#{my_messages.downcase.parameterize}']"
end

def has_my_messages_link?(text = my_messages)
page.has_css?(
".sidebar-section-link[data-link-name='#{my_messages.downcase.parameterize}']",
text:,
)
page.has_css?(my_messages_link_css, text:)
end

def has_no_my_messages_link?
page.has_no_css?(
".sidebar-section-link[data-link-name='#{my_messages.downcase.parameterize}']",
text: my_messages,
)
def has_no_my_messages_link?(text = my_messages)
page.has_no_css?(my_messages_link_css, text:)
end

def has_my_messages_link_with_unread_icon?
page.has_css?("#{my_messages_link_css} .sidebar-section-link-suffix.icon.unread")
end

def has_my_messages_link_without_unread_icon?
page.has_no_css?("#{my_messages_link_css} .sidebar-section-link-suffix.icon.unread")
end

def has_my_messages_link_with_unread_count?
page.has_css?("#{my_messages_link_css} .sidebar-section-link-content-badge")
end
end
end
Expand Down
4 changes: 4 additions & 0 deletions spec/system/page_objects/pages/user_private_messages.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ def visit_group_inbox(user, group)
def has_right_inbox_dropdown_value?(value)
has_css?(".user-nav-messages-dropdown .combo-box-header[data-name='#{value}']")
end

def click_unseen_private_mesage(topic_id)
find("tr[data-topic-id='#{topic_id}'] a[data-topic-id='#{topic_id}']").click
end
end
end
end
30 changes: 29 additions & 1 deletion spec/system/viewing_sidebar_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@
end
end

describe "My messages sidebar link" do
describe "when My messages sidebar link" do
it "should show for user with `can_send_private_messages` permission" do
sign_in(admin)
visit("/")
Expand Down Expand Up @@ -196,6 +196,34 @@
expect(sidebar).to have_my_messages_link("Overrided")
end
end

describe "has unread messages" do
fab!(:private_message) do
Fabricate(:private_message_post, user: user, recipient: admin).topic
end
let(:user_private_messages_page) { PageObjects::Pages::UserPrivateMessages.new }

it "should show new messages indicator" do
sign_in(admin)
visit("/")
expect(sidebar).to have_my_messages_link_with_unread_icon
end

it "should show a count of the new items" do
admin.user_option.update!(sidebar_show_count_of_new_items: true)
sign_in(admin)
visit("/")
expect(sidebar).to have_my_messages_link_with_unread_count
end

it "should remove unread icon after all messages are read" do
sign_in(admin)
user_private_messages_page.visit(admin)
user_private_messages_page.click_unseen_private_mesage(private_message.id)
expect(sidebar).to have_my_messages_link_with_unread_icon
try_until_success { expect(sidebar).to have_my_messages_link_without_unread_icon }
end
end
end

it "shouldn't display the panel header for the main sidebar" do
Expand Down
Loading