diff --git a/assets/icons/bell_badged.svg b/assets/icons/bell_badged.svg new file mode 100644 index 0000000000..0c841dfcfd --- /dev/null +++ b/assets/icons/bell_badged.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/crates/collab_ui/src/notification_panel.rs b/crates/collab_ui/src/notification_panel.rs index 6f4ee1baae..24bbcbc027 100644 --- a/crates/collab_ui/src/notification_panel.rs +++ b/crates/collab_ui/src/notification_panel.rs @@ -47,6 +47,7 @@ pub struct NotificationPanel { local_timezone: UtcOffset, focus_handle: FocusHandle, mark_as_read_tasks: HashMap>>, + unseen_notifications: Vec, } #[derive(Serialize, Deserialize)] @@ -141,6 +142,7 @@ impl NotificationPanel { active: false, mark_as_read_tasks: HashMap::default(), width: None, + unseen_notifications: Vec::new(), }; let mut old_dock_position = this.position(cx); @@ -441,6 +443,10 @@ impl NotificationPanel { } fn is_showing_notification(&self, notification: &Notification, cx: &ViewContext) -> bool { + if !self.active { + return false; + } + if let Notification::ChannelMessageMention { channel_id, .. } = ¬ification { if let Some(workspace) = self.workspace.upgrade() { return if let Some(panel) = workspace.read(cx).panel::(cx) { @@ -465,9 +471,17 @@ impl NotificationPanel { cx: &mut ViewContext, ) { match event { - NotificationEvent::NewNotification { entry } => self.add_toast(entry, cx), + NotificationEvent::NewNotification { entry } => { + if !self.is_showing_notification(&entry.notification, cx) { + self.unseen_notifications.push(entry.clone()); + } + self.add_toast(entry, cx); + } NotificationEvent::NotificationRemoved { entry } - | NotificationEvent::NotificationRead { entry } => self.remove_toast(entry.id, cx), + | NotificationEvent::NotificationRead { entry } => { + self.unseen_notifications.retain(|n| n.id != entry.id); + self.remove_toast(entry.id, cx); + } NotificationEvent::NotificationsUpdated { old_range, new_count, @@ -650,15 +664,28 @@ impl Panel for NotificationPanel { fn set_active(&mut self, active: bool, cx: &mut ViewContext) { self.active = active; + + if self.active { + self.unseen_notifications = Vec::new(); + cx.notify(); + } + if self.notification_store.read(cx).notification_count() == 0 { cx.emit(Event::Dismissed); } } fn icon(&self, cx: &gpui::WindowContext) -> Option { - (NotificationPanelSettings::get_global(cx).button - && self.notification_store.read(cx).notification_count() > 0) - .then(|| IconName::Bell) + let show_button = NotificationPanelSettings::get_global(cx).button; + if !show_button { + return None; + } + + if self.unseen_notifications.is_empty() { + return Some(IconName::Bell); + } + + Some(IconName::BellBadged) } fn icon_tooltip(&self, _cx: &WindowContext) -> Option<&'static str> { diff --git a/crates/ui/src/components/icon.rs b/crates/ui/src/components/icon.rs index bdc691dc9a..3b8f3bc8ae 100644 --- a/crates/ui/src/components/icon.rs +++ b/crates/ui/src/components/icon.rs @@ -39,6 +39,7 @@ pub enum IconName { Bell, BellOff, BellRing, + BellBadged, Bolt, CaseSensitive, Check, @@ -130,6 +131,7 @@ impl IconName { IconName::Bell => "icons/bell.svg", IconName::BellOff => "icons/bell_off.svg", IconName::BellRing => "icons/bell_ring.svg", + IconName::BellBadged => "icons/bell_badged.svg", IconName::Bolt => "icons/bolt.svg", IconName::CaseSensitive => "icons/case_insensitive.svg", IconName::Check => "icons/check.svg",