diff --git a/Cargo.lock b/Cargo.lock index 5b465d8131..f1254538f5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -935,6 +935,7 @@ checksum = "f92cfa0fd5690b3cf8c1ef2cabbd9b7ef22fa53cf5e1f92b05103f6d5d1cf6e7" name = "contacts_panel" version = "0.1.0" dependencies = [ + "anyhow", "client", "editor", "futures", diff --git a/crates/client/src/user.rs b/crates/client/src/user.rs index d11ae58bda..b8a320f524 100644 --- a/crates/client/src/user.rs +++ b/crates/client/src/user.rs @@ -78,10 +78,12 @@ pub struct InviteInfo { pub url: Arc, } -#[derive(Clone)] -pub struct ContactEvent { - pub user: Arc, - pub kind: ContactEventKind, +pub enum Event { + Contact { + user: Arc, + kind: ContactEventKind, + }, + ShowContacts, } #[derive(Clone, Copy)] @@ -92,7 +94,7 @@ pub enum ContactEventKind { } impl Entity for UserStore { - type Event = ContactEvent; + type Event = Event; } enum UpdateContacts { @@ -111,6 +113,7 @@ impl UserStore { let rpc_subscriptions = vec![ client.add_message_handler(cx.handle(), Self::handle_update_contacts), client.add_message_handler(cx.handle(), Self::handle_update_invite_info), + client.add_message_handler(cx.handle(), Self::handle_show_contacts), ]; Self { users: Default::default(), @@ -180,6 +183,16 @@ impl UserStore { Ok(()) } + async fn handle_show_contacts( + this: ModelHandle, + _: TypedEnvelope, + _: Arc, + mut cx: AsyncAppContext, + ) -> Result<()> { + this.update(&mut cx, |_, cx| cx.emit(Event::ShowContacts)); + Ok(()) + } + pub fn invite_info(&self) -> Option<&InviteInfo> { self.invite_info.as_ref() } @@ -274,7 +287,7 @@ impl UserStore { // Update existing contacts and insert new ones for (updated_contact, should_notify) in updated_contacts { if should_notify { - cx.emit(ContactEvent { + cx.emit(Event::Contact { user: updated_contact.user.clone(), kind: ContactEventKind::Accepted, }); @@ -291,7 +304,7 @@ impl UserStore { // Remove incoming contact requests this.incoming_contact_requests.retain(|user| { if removed_incoming_requests.contains(&user.id) { - cx.emit(ContactEvent { + cx.emit(Event::Contact { user: user.clone(), kind: ContactEventKind::Cancelled, }); @@ -303,7 +316,7 @@ impl UserStore { // Update existing incoming requests and insert new ones for (user, should_notify) in incoming_requests { if should_notify { - cx.emit(ContactEvent { + cx.emit(Event::Contact { user: user.clone(), kind: ContactEventKind::Requested, }); diff --git a/crates/contacts_panel/Cargo.toml b/crates/contacts_panel/Cargo.toml index b6d7bf63fc..800bad497d 100644 --- a/crates/contacts_panel/Cargo.toml +++ b/crates/contacts_panel/Cargo.toml @@ -18,6 +18,7 @@ settings = { path = "../settings" } theme = { path = "../theme" } util = { path = "../util" } workspace = { path = "../workspace" } +anyhow = "1.0" futures = "0.3" log = "0.4" postage = { version = "0.4.1", features = ["futures-traits"] } diff --git a/crates/contacts_panel/src/contact_notification.rs b/crates/contacts_panel/src/contact_notification.rs index 2d408da0c2..a1eeb365f5 100644 --- a/crates/contacts_panel/src/contact_notification.rs +++ b/crates/contacts_panel/src/contact_notification.rs @@ -1,5 +1,7 @@ +use std::sync::Arc; + use crate::notifications::render_user_notification; -use client::{ContactEvent, ContactEventKind, UserStore}; +use client::{ContactEventKind, User, UserStore}; use gpui::{ elements::*, impl_internal_actions, Entity, ModelHandle, MutableAppContext, RenderContext, View, ViewContext, @@ -15,7 +17,8 @@ pub fn init(cx: &mut MutableAppContext) { pub struct ContactNotification { user_store: ModelHandle, - event: ContactEvent, + user: Arc, + kind: client::ContactEventKind, } #[derive(Clone)] @@ -41,27 +44,27 @@ impl View for ContactNotification { } fn render(&mut self, cx: &mut RenderContext) -> ElementBox { - match self.event.kind { + match self.kind { ContactEventKind::Requested => render_user_notification( - self.event.user.clone(), + self.user.clone(), "wants to add you as a contact", Some("They won't know if you decline."), RespondToContactRequest { - user_id: self.event.user.id, + user_id: self.user.id, accept: false, }, vec![ ( "Decline", Box::new(RespondToContactRequest { - user_id: self.event.user.id, + user_id: self.user.id, accept: false, }), ), ( "Accept", Box::new(RespondToContactRequest { - user_id: self.event.user.id, + user_id: self.user.id, accept: true, }), ), @@ -69,10 +72,10 @@ impl View for ContactNotification { cx, ), ContactEventKind::Accepted => render_user_notification( - self.event.user.clone(), + self.user.clone(), "accepted your contact request", None, - Dismiss(self.event.user.id), + Dismiss(self.user.id), vec![], cx, ), @@ -89,30 +92,35 @@ impl Notification for ContactNotification { impl ContactNotification { pub fn new( - event: ContactEvent, + user: Arc, + kind: client::ContactEventKind, user_store: ModelHandle, cx: &mut ViewContext, ) -> Self { cx.subscribe(&user_store, move |this, _, event, cx| { - if let client::ContactEvent { + if let client::Event::Contact { kind: ContactEventKind::Cancelled, user, } = event { - if user.id == this.event.user.id { + if user.id == this.user.id { cx.emit(Event::Dismiss); } } }) .detach(); - Self { event, user_store } + Self { + user, + kind, + user_store, + } } fn dismiss(&mut self, _: &Dismiss, cx: &mut ViewContext) { self.user_store.update(cx, |store, cx| { store - .dismiss_contact_request(self.event.user.id, cx) + .dismiss_contact_request(self.user.id, cx) .detach_and_log_err(cx); }); cx.emit(Event::Dismiss); diff --git a/crates/contacts_panel/src/contacts_panel.rs b/crates/contacts_panel/src/contacts_panel.rs index 95b4cf20e6..a1d1d31aa4 100644 --- a/crates/contacts_panel/src/contacts_panel.rs +++ b/crates/contacts_panel/src/contacts_panel.rs @@ -158,16 +158,28 @@ impl ContactsPanel { if let Some((workspace, user_store)) = workspace.upgrade(cx).zip(user_store.upgrade(cx)) { - workspace.update(cx, |workspace, cx| match event.kind { - ContactEventKind::Requested | ContactEventKind::Accepted => workspace - .show_notification(event.user.id as usize, cx, |cx| { - cx.add_view(|cx| { - ContactNotification::new(event.clone(), user_store, cx) - }) - }), + workspace.update(cx, |workspace, cx| match event { + client::Event::Contact { user, kind } => match kind { + ContactEventKind::Requested | ContactEventKind::Accepted => workspace + .show_notification(user.id as usize, cx, |cx| { + cx.add_view(|cx| { + ContactNotification::new( + user.clone(), + *kind, + user_store, + cx, + ) + }) + }), + _ => {} + }, _ => {} }); } + + if let client::Event::ShowContacts = event { + cx.emit(Event::Activate); + } } }) .detach(); @@ -801,6 +813,10 @@ impl SidebarItem for ContactsPanel { fn contains_focused_view(&self, cx: &AppContext) -> bool { self.filter_editor.is_focused(cx) } + + fn should_activate_item_on_event(&self, event: &Event, _: &AppContext) -> bool { + matches!(event, Event::Activate) + } } fn render_icon_button(style: &IconButton, svg_path: &'static str) -> impl Element { @@ -816,7 +832,9 @@ fn render_icon_button(style: &IconButton, svg_path: &'static str) -> impl Elemen .with_height(style.button_width) } -pub enum Event {} +pub enum Event { + Activate, +} impl Entity for ContactsPanel { type Event = Event; diff --git a/crates/workspace/src/sidebar.rs b/crates/workspace/src/sidebar.rs index 685782a2d2..afdacc2a31 100644 --- a/crates/workspace/src/sidebar.rs +++ b/crates/workspace/src/sidebar.rs @@ -9,6 +9,9 @@ use std::{cell::RefCell, rc::Rc}; use theme::Theme; pub trait SidebarItem: View { + fn should_activate_item_on_event(&self, _: &Self::Event, _: &AppContext) -> bool { + false + } fn should_show_badge(&self, cx: &AppContext) -> bool; fn contains_focused_view(&self, _: &AppContext) -> bool { false @@ -16,6 +19,7 @@ pub trait SidebarItem: View { } pub trait SidebarItemHandle { + fn id(&self) -> usize; fn should_show_badge(&self, cx: &AppContext) -> bool; fn is_focused(&self, cx: &AppContext) -> bool; fn to_any(&self) -> AnyViewHandle; @@ -25,6 +29,10 @@ impl SidebarItemHandle for ViewHandle where T: SidebarItem, { + fn id(&self) -> usize { + self.id() + } + fn should_show_badge(&self, cx: &AppContext) -> bool { self.read(cx).should_show_badge(cx) } @@ -61,7 +69,7 @@ pub enum Side { struct Item { icon_path: &'static str, view: Rc, - _observation: Subscription, + _subscriptions: [Subscription; 2], } pub struct SidebarButtons { @@ -99,11 +107,24 @@ impl Sidebar { view: ViewHandle, cx: &mut ViewContext, ) { - let subscription = cx.observe(&view, |_, _, cx| cx.notify()); + let subscriptions = [ + cx.observe(&view, |_, _, cx| cx.notify()), + cx.subscribe(&view, |this, view, event, cx| { + if view.read(cx).should_activate_item_on_event(event, cx) { + if let Some(ix) = this + .items + .iter() + .position(|item| item.view.id() == view.id()) + { + this.activate_item(ix, cx); + } + } + }), + ]; self.items.push(Item { icon_path, view: Rc::new(view), - _observation: subscription, + _subscriptions: subscriptions, }); cx.notify() }