feat: show unread notification count in sidebar

This commit is contained in:
Kaushal Shriwas 2026-04-16 23:41:34 +05:30
parent d8ad02d643
commit e878ab1d7d
4 changed files with 46 additions and 0 deletions

View file

@ -207,6 +207,11 @@ def trigger_indicator_hide():
frappe.publish_realtime("indicator_hide", user=frappe.session.user)
@frappe.whitelist()
def get_unread_count():
return frappe.db.count("Notification Log", {"read": 0, "for_user": frappe.session.user})
def set_notifications_as_unseen(user):
try:
frappe.db.set_value("Notification Settings", user, "seen", 0, update_modified=False)

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 = 0;
if (this.settings && this.settings.seen == 0) {
this.toggle_notification_icon(false);
}
this.fetch_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,20 @@ 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).removeClass("hidden");
} else {
$suffix.addClass("hidden");
}
}
toggle_seen(flag) {
frappe.call(
"frappe.desk.doctype.notification_settings.notification_settings.set_seen_value",
@ -400,10 +418,21 @@ class NotificationsView extends BaseNotificationsView {
);
}
fetch_unread_count() {
frappe
.call("frappe.desk.doctype.notification_log.notification_log.get_unread_count")
.then((r) => {
const count = r.message || 0;
this.unread_count = count;
this.update_count_badge(count);
});
}
setup_notification_listeners() {
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'></span>",
onClick: () => {
const $dropdown = this.wrapper.find(".dropdown-notifications");
$dropdown.toggleClass("hidden");

View file

@ -60,6 +60,17 @@
}
}
.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 .item-anchor {
overflow: visible;
}