Add a way to clear activity indicator (#30156)

Kirill Bulatov created

Follow-up of https://github.com/zed-industries/zed/pull/30015

* Restyles the dismiss and close buttons a bit: change the dismiss icon
and add tooltips with the bindings to both
* Allows ESC to clear any status that's in the activity indicator now,
if all notifications are cleared: this won't suppress any further status
additions though, so statuses may resurface later

Release Notes:

- Added a way to clear activity indicator

Change summary

crates/activity_indicator/src/activity_indicator.rs | 28 +++++-
crates/auto_update/src/auto_update.rs               |  6 +
crates/collab_ui/src/notification_panel.rs          | 13 ++
crates/workspace/src/notifications.rs               | 58 ++++++++++++--
crates/workspace/src/workspace.rs                   |  2 
5 files changed, 88 insertions(+), 19 deletions(-)

Detailed changes

crates/activity_indicator/src/activity_indicator.rs 🔗

@@ -43,6 +43,7 @@ pub struct ActivityIndicator {
     context_menu_handle: PopoverMenuHandle<ContextMenu>,
 }
 
+#[derive(Debug)]
 struct ServerStatus {
     name: SharedString,
     status: BinaryStatus,
@@ -70,6 +71,7 @@ impl ActivityIndicator {
     ) -> Entity<ActivityIndicator> {
         let project = workspace.project().clone();
         let auto_updater = AutoUpdater::get(cx);
+        let workspace_handle = cx.entity();
         let this = cx.new(|cx| {
             let mut status_events = languages.language_server_binary_statuses();
             cx.spawn(async move |this, cx| {
@@ -84,6 +86,25 @@ impl ActivityIndicator {
             })
             .detach();
 
+            cx.subscribe_in(
+                &workspace_handle,
+                window,
+                |activity_indicator, _, event, window, cx| match event {
+                    workspace::Event::ClearActivityIndicator { .. } => {
+                        if activity_indicator.statuses.pop().is_some() {
+                            activity_indicator.dismiss_error_message(
+                                &DismissErrorMessage,
+                                window,
+                                cx,
+                            );
+                            cx.notify();
+                        }
+                    }
+                    _ => {}
+                },
+            )
+            .detach();
+
             cx.subscribe(
                 &project.read(cx).lsp_store(),
                 |_, _, event, cx| match event {
@@ -115,7 +136,7 @@ impl ActivityIndicator {
             }
 
             Self {
-                statuses: Default::default(),
+                statuses: Vec::new(),
                 project: project.clone(),
                 auto_updater,
                 context_menu_handle: Default::default(),
@@ -185,11 +206,8 @@ impl ActivityIndicator {
         cx: &mut Context<Self>,
     ) {
         if let Some(updater) = &self.auto_updater {
-            updater.update(cx, |updater, cx| {
-                updater.dismiss_error(cx);
-            });
+            updater.update(cx, |updater, cx| updater.dismiss_error(cx));
         }
-        cx.notify();
     }
 
     fn pending_language_server_work<'a>(

crates/auto_update/src/auto_update.rs 🔗

@@ -335,9 +335,13 @@ impl AutoUpdater {
         self.status.clone()
     }
 
-    pub fn dismiss_error(&mut self, cx: &mut Context<Self>) {
+    pub fn dismiss_error(&mut self, cx: &mut Context<Self>) -> bool {
+        if self.status == AutoUpdateStatus::Idle {
+            return false;
+        }
         self.status = AutoUpdateStatus::Idle;
         cx.notify();
+        true
     }
 
     // If you are packaging Zed and need to override the place it downloads SSH remotes from,

crates/collab_ui/src/notification_panel.rs 🔗

@@ -22,6 +22,7 @@ use ui::{
     Avatar, Button, Icon, IconButton, IconName, Label, Tab, Tooltip, h_flex, prelude::*, v_flex,
 };
 use util::{ResultExt, TryFutureExt};
+use workspace::SuppressNotification;
 use workspace::notifications::{
     Notification as WorkspaceNotification, NotificationId, SuppressEvent,
 };
@@ -823,11 +824,19 @@ impl Render for NotificationToast {
             .child(Label::new(self.text.clone()))
             .child(
                 IconButton::new("close", IconName::Close)
+                    .tooltip(|window, cx| Tooltip::for_action("Close", &menu::Cancel, window, cx))
                     .on_click(cx.listener(|_, _, _, cx| cx.emit(DismissEvent))),
             )
             .child(
-                IconButton::new("suppress", IconName::XCircle)
-                    .tooltip(Tooltip::text("Do not show until restart"))
+                IconButton::new("suppress", IconName::SquareMinus)
+                    .tooltip(|window, cx| {
+                        Tooltip::for_action(
+                            "Do not show until restart",
+                            &SuppressNotification,
+                            window,
+                            cx,
+                        )
+                    })
                     .on_click(cx.listener(|_, _, _, cx| cx.emit(SuppressEvent))),
             )
             .on_click(cx.listener(|this, _, window, cx| {

crates/workspace/src/notifications.rs 🔗

@@ -1,4 +1,4 @@
-use crate::{Toast, Workspace};
+use crate::{SuppressNotification, Toast, Workspace};
 use gpui::{
     AnyView, App, AppContext as _, AsyncWindowContext, ClipboardItem, Context, DismissEvent,
     Entity, EventEmitter, FocusHandle, Focusable, PromptLevel, Render, ScrollHandle, Task, svg,
@@ -290,8 +290,15 @@ impl Render for LanguageServerPrompt {
                                 h_flex()
                                     .gap_2()
                                     .child(
-                                        IconButton::new("suppress", IconName::XCircle)
-                                            .tooltip(Tooltip::text("Do not show until restart"))
+                                        IconButton::new("suppress", IconName::SquareMinus)
+                                            .tooltip(|window, cx| {
+                                                Tooltip::for_action(
+                                                    "Do not show until restart",
+                                                    &SuppressNotification,
+                                                    window,
+                                                    cx,
+                                                )
+                                            })
                                             .on_click(
                                                 cx.listener(|_, _, _, cx| cx.emit(SuppressEvent)),
                                             ),
@@ -308,9 +315,20 @@ impl Render for LanguageServerPrompt {
                                             })
                                             .tooltip(Tooltip::text("Copy Description")),
                                     )
-                                    .child(IconButton::new("close", IconName::Close).on_click(
-                                        cx.listener(|_, _, _, cx| cx.emit(gpui::DismissEvent)),
-                                    )),
+                                    .child(
+                                        IconButton::new("close", IconName::Close)
+                                            .tooltip(|window, cx| {
+                                                Tooltip::for_action(
+                                                    "Close",
+                                                    &menu::Cancel,
+                                                    window,
+                                                    cx,
+                                                )
+                                            })
+                                            .on_click(cx.listener(|_, _, _, cx| {
+                                                cx.emit(gpui::DismissEvent)
+                                            })),
+                                    ),
                             ),
                     )
                     .child(Label::new(request.message.to_string()).size(LabelSize::Small))
@@ -443,6 +461,8 @@ pub mod simple_message_notification {
     };
     use ui::{Tooltip, prelude::*};
 
+    use crate::SuppressNotification;
+
     use super::{Notification, SuppressEvent};
 
     pub struct MessageNotification {
@@ -640,8 +660,15 @@ pub mod simple_message_notification {
                                 .gap_2()
                                 .when(self.show_suppress_button, |this| {
                                     this.child(
-                                        IconButton::new("suppress", IconName::XCircle)
-                                            .tooltip(Tooltip::text("Do not show until restart"))
+                                        IconButton::new("suppress", IconName::SquareMinus)
+                                            .tooltip(|window, cx| {
+                                                Tooltip::for_action(
+                                                    "Do not show until restart",
+                                                    &SuppressNotification,
+                                                    window,
+                                                    cx,
+                                                )
+                                            })
                                             .on_click(cx.listener(|_, _, _, cx| {
                                                 cx.emit(SuppressEvent);
                                             })),
@@ -649,9 +676,18 @@ pub mod simple_message_notification {
                                 })
                                 .when(self.show_close_button, |this| {
                                     this.child(
-                                        IconButton::new("close", IconName::Close).on_click(
-                                            cx.listener(|this, _, _, cx| this.dismiss(cx)),
-                                        ),
+                                        IconButton::new("close", IconName::Close)
+                                            .tooltip(|window, cx| {
+                                                Tooltip::for_action(
+                                                    "Close",
+                                                    &menu::Cancel,
+                                                    window,
+                                                    cx,
+                                                )
+                                            })
+                                            .on_click(
+                                                cx.listener(|this, _, _, cx| this.dismiss(cx)),
+                                            ),
                                     )
                                 }),
                         ),

crates/workspace/src/workspace.rs 🔗

@@ -869,6 +869,7 @@ pub enum Event {
     },
     ZoomChanged,
     ModalOpened,
+    ClearActivityIndicator,
 }
 
 #[derive(Debug)]
@@ -5496,6 +5497,7 @@ impl Workspace {
             return;
         }
 
+        cx.emit(Event::ClearActivityIndicator);
         cx.propagate();
     }
 }