Dismiss contact request notification if request is cancelled

Nathan Sobo created

Change summary

crates/client/src/user.rs                          | 15 +++-
crates/contacts_panel/src/contact_notifications.rs | 58 ++++++++++++++++
crates/contacts_panel/src/contacts_panel.rs        | 10 +-
crates/contacts_panel/src/notifications.rs         | 36 ---------
crates/workspace/src/workspace.rs                  | 26 ++++++
5 files changed, 100 insertions(+), 45 deletions(-)

Detailed changes

crates/client/src/user.rs 🔗

@@ -55,7 +55,8 @@ pub struct UserStore {
 }
 
 pub enum Event {
-    NotifyIncomingRequest(Arc<User>),
+    ContactRequested(Arc<User>),
+    ContactRequestCancelled(Arc<User>),
 }
 
 impl Entity for UserStore {
@@ -225,12 +226,18 @@ impl UserStore {
                         }
 
                         // Remove incoming contact requests
-                        this.incoming_contact_requests
-                            .retain(|user| !removed_incoming_requests.contains(&user.id));
+                        this.incoming_contact_requests.retain(|user| {
+                            if removed_incoming_requests.contains(&user.id) {
+                                cx.emit(Event::ContactRequestCancelled(user.clone()));
+                                false
+                            } else {
+                                true
+                            }
+                        });
                         // Update existing incoming requests and insert new ones
                         for (user, should_notify) in incoming_requests {
                             if should_notify {
-                                cx.emit(Event::NotifyIncomingRequest(user.clone()));
+                                cx.emit(Event::ContactRequested(user.clone()));
                             }
 
                             match this

crates/contacts_panel/src/contact_notifications.rs 🔗

@@ -0,0 +1,58 @@
+use client::{User, UserStore};
+use gpui::{color::Color, elements::*, Entity, ModelHandle, View, ViewContext};
+use std::sync::Arc;
+use workspace::Notification;
+
+pub struct IncomingRequestNotification {
+    user: Arc<User>,
+    user_store: ModelHandle<UserStore>,
+}
+
+pub enum Event {
+    Dismiss,
+}
+
+impl Entity for IncomingRequestNotification {
+    type Event = Event;
+}
+
+impl View for IncomingRequestNotification {
+    fn ui_name() -> &'static str {
+        "IncomingRequestNotification"
+    }
+
+    fn render(&mut self, cx: &mut gpui::RenderContext<'_, Self>) -> ElementBox {
+        Empty::new()
+            .constrained()
+            .with_height(200.)
+            .contained()
+            .with_background_color(Color::red())
+            .boxed()
+    }
+}
+
+impl Notification for IncomingRequestNotification {
+    fn should_dismiss_notification_on_event(&self, event: &<Self as Entity>::Event) -> bool {
+        matches!(event, Event::Dismiss)
+    }
+}
+
+impl IncomingRequestNotification {
+    pub fn new(
+        user: Arc<User>,
+        user_store: ModelHandle<UserStore>,
+        cx: &mut ViewContext<Self>,
+    ) -> Self {
+        let user_id = user.id;
+        cx.subscribe(&user_store, move |_, _, event, cx| {
+            if let client::Event::ContactRequestCancelled(user) = event {
+                if user.id == user_id {
+                    cx.emit(Event::Dismiss);
+                }
+            }
+        })
+        .detach();
+
+        Self { user, user_store }
+    }
+}

crates/contacts_panel/src/contacts_panel.rs 🔗

@@ -1,7 +1,8 @@
 mod contact_finder;
-mod notifications;
+mod contact_notifications;
 
 use client::{Contact, User, UserStore};
+use contact_notifications::IncomingRequestNotification;
 use editor::{Cancel, Editor};
 use fuzzy::{match_strings, StringMatchCandidate};
 use gpui::{
@@ -12,7 +13,6 @@ use gpui::{
     Element, ElementBox, Entity, LayoutContext, ModelHandle, MutableAppContext, RenderContext,
     Subscription, View, ViewContext, ViewHandle, WeakViewHandle,
 };
-use notifications::IncomingRequestNotification;
 use serde::Deserialize;
 use settings::Settings;
 use std::sync::Arc;
@@ -86,14 +86,15 @@ impl ContactsPanel {
         cx.subscribe(&app_state.user_store, {
             let user_store = app_state.user_store.clone();
             move |_, _, event, cx| match event {
-                client::Event::NotifyIncomingRequest(user) => {
+                client::Event::ContactRequested(user) => {
                     if let Some(workspace) = workspace.upgrade(cx) {
                         workspace.update(cx, |workspace, cx| {
                             workspace.show_notification(
-                                cx.add_view(|_| {
+                                cx.add_view(|cx| {
                                     IncomingRequestNotification::new(
                                         user.clone(),
                                         user_store.clone(),
+                                        cx,
                                     )
                                 }),
                                 cx,
@@ -101,6 +102,7 @@ impl ContactsPanel {
                         })
                     }
                 }
+                _ => {}
             }
         })
         .detach();

crates/contacts_panel/src/notifications.rs 🔗

@@ -1,36 +0,0 @@
-use client::{User, UserStore};
-use gpui::{color::Color, elements::*, Entity, ModelHandle, View};
-use std::sync::Arc;
-use workspace::Notification;
-
-pub struct IncomingRequestNotification {
-    user: Arc<User>,
-    user_store: ModelHandle<UserStore>,
-}
-
-impl Entity for IncomingRequestNotification {
-    type Event = ();
-}
-
-impl View for IncomingRequestNotification {
-    fn ui_name() -> &'static str {
-        "IncomingRequestNotification"
-    }
-
-    fn render(&mut self, cx: &mut gpui::RenderContext<'_, Self>) -> ElementBox {
-        Empty::new()
-            .constrained()
-            .with_height(200.)
-            .contained()
-            .with_background_color(Color::red())
-            .boxed()
-    }
-}
-
-impl Notification for IncomingRequestNotification {}
-
-impl IncomingRequestNotification {
-    pub fn new(user: Arc<User>, user_store: ModelHandle<UserStore>) -> Self {
-        Self { user, user_store }
-    }
-}

crates/workspace/src/workspace.rs 🔗

@@ -604,13 +604,20 @@ impl<T: Item> WeakItemHandle for WeakViewHandle<T> {
     }
 }
 
-pub trait Notification: View {}
+pub trait Notification: View {
+    fn should_dismiss_notification_on_event(&self, event: &<Self as Entity>::Event) -> bool;
+}
 
 pub trait NotificationHandle {
+    fn id(&self) -> usize;
     fn to_any(&self) -> AnyViewHandle;
 }
 
 impl<T: Notification> NotificationHandle for ViewHandle<T> {
+    fn id(&self) -> usize {
+        self.id()
+    }
+
     fn to_any(&self) -> AnyViewHandle {
         self.into()
     }
@@ -996,10 +1003,27 @@ impl Workspace {
         notification: ViewHandle<V>,
         cx: &mut ViewContext<Self>,
     ) {
+        cx.subscribe(&notification, |this, handle, event, cx| {
+            if handle.read(cx).should_dismiss_notification_on_event(event) {
+                this.dismiss_notification(handle.id(), cx);
+            }
+        })
+        .detach();
         self.notifications.push(Box::new(notification));
         cx.notify();
     }
 
+    fn dismiss_notification(&mut self, id: usize, cx: &mut ViewContext<Self>) {
+        self.notifications.retain(|handle| {
+            if handle.id() == id {
+                cx.notify();
+                false
+            } else {
+                true
+            }
+        });
+    }
+
     pub fn items<'a>(
         &'a self,
         cx: &'a AppContext,