Merge pull request #38658 from kaulith/feat/sidebar-notification-unread-count

feat: show unread notification count in sidebar
This commit is contained in:
Ejaaz Khan 2026-04-18 23:49:09 +05:30 committed by GitHub
commit 6ebe8e2b8d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 37 additions and 0 deletions

View file

@ -78,6 +78,9 @@ def get_bootinfo():
bootinfo.home_folder = frappe.db.get_value("File", {"is_home_folder": 1})
bootinfo.navbar_settings = get_navbar_settings()
bootinfo.notification_settings = get_notification_settings()
bootinfo.notification_unread_count = frappe.db.count(
"Notification Log", {"read": 0, "for_user": frappe.session.user}
)
bootinfo.onboarding_tours = get_onboarding_ui_tours()
set_time_zone(bootinfo)

View file

@ -128,6 +128,7 @@ frappe.ui.Notifications = class Notifications {
e.stopImmediatePropagation();
this.dropdown_list.find(".unread").removeClass("unread");
frappe.call("frappe.desk.doctype.notification_log.notification_log.mark_all_as_read");
this.tabs.notifications?.update_count_badge(0);
}
setup_dropdown_events() {
@ -233,10 +234,12 @@ class NotificationsView extends BaseNotificationsView {
this.dropdown_items = [];
this.notifications_fetched = false;
this.unread_count = frappe.boot.notification_unread_count || 0;
if (this.settings && this.settings.seen == 0) {
this.toggle_notification_icon(false);
}
this.update_count_badge(this.unread_count);
}
update_dropdown() {
@ -272,6 +275,7 @@ class NotificationsView extends BaseNotificationsView {
})
.then(() => {
$el.removeClass("unread");
this.update_count_badge(Math.max(this.unread_count - 1, 0));
});
}
@ -390,6 +394,23 @@ class NotificationsView extends BaseNotificationsView {
this.bell_indicator?.toggleClass("indicator blue", !seen);
}
update_count_badge(count) {
this.unread_count = count;
const $suffix = this.parent
.closest(".body-sidebar")
?.find(".sidebar-notification .sidebar-notification-count");
if (!$suffix?.length) return;
if (count > 0) {
$suffix
.text(count > 99 ? "99+" : count)
.attr("aria-label", __("{0} unread notifications", [count]))
.removeClass("hidden");
} else {
$suffix.removeAttr("aria-label").addClass("hidden");
}
}
toggle_seen(flag) {
frappe.call(
"frappe.desk.doctype.notification_settings.notification_settings.set_seen_value",
@ -404,6 +425,7 @@ class NotificationsView extends BaseNotificationsView {
frappe.realtime.on("notification", () => {
this.settings.seen = 0;
this.toggle_notification_icon(false);
this.update_count_badge(this.unread_count + 1);
this.update_dropdown();
});

View file

@ -487,6 +487,7 @@ frappe.ui.Sidebar = class Sidebar {
standard: true,
type: "Button",
class: "sidebar-notification hidden",
suffix: "<span class='sidebar-notification-count hidden' aria-live='polite'></span>",
onClick: () => {
const $dropdown = this.wrapper.find(".dropdown-notifications");
$dropdown.toggleClass("hidden");

View file

@ -75,6 +75,17 @@
opacity: 0;
}
.sidebar-notification-count {
min-width: 20px;
padding: 1px 7px;
background-color: var(--bg-gray-100);
color: var(--text-muted);
font-size: var(--text-sm);
line-height: 1.2;
text-align: center;
border-radius: 10px;
}
.sidebar-notification .standard-sidebar-item .item-anchor {
overflow: visible;
}