Cargo.lock 🔗
@@ -935,6 +935,7 @@ checksum = "f92cfa0fd5690b3cf8c1ef2cabbd9b7ef22fa53cf5e1f92b05103f6d5d1cf6e7"
name = "contacts_panel"
version = "0.1.0"
dependencies = [
+ "anyhow",
"client",
"editor",
"futures",
Antonio Scandurra and Nathan Sobo created
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
Cargo.lock | 1
crates/client/src/user.rs | 29 +++++++++---
crates/contacts_panel/Cargo.toml | 1
crates/contacts_panel/src/contact_notification.rs | 36 ++++++++++------
crates/contacts_panel/src/contacts_panel.rs | 34 ++++++++++++---
crates/workspace/src/sidebar.rs | 27 +++++++++++-
6 files changed, 95 insertions(+), 33 deletions(-)
@@ -935,6 +935,7 @@ checksum = "f92cfa0fd5690b3cf8c1ef2cabbd9b7ef22fa53cf5e1f92b05103f6d5d1cf6e7"
name = "contacts_panel"
version = "0.1.0"
dependencies = [
+ "anyhow",
"client",
"editor",
"futures",
@@ -78,10 +78,12 @@ pub struct InviteInfo {
pub url: Arc<str>,
}
-#[derive(Clone)]
-pub struct ContactEvent {
- pub user: Arc<User>,
- pub kind: ContactEventKind,
+pub enum Event {
+ Contact {
+ user: Arc<User>,
+ 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<Self>,
+ _: TypedEnvelope<proto::ShowContacts>,
+ _: Arc<Client>,
+ 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,
});
@@ -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"] }
@@ -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<UserStore>,
- event: ContactEvent,
+ user: Arc<User>,
+ kind: client::ContactEventKind,
}
#[derive(Clone)]
@@ -41,27 +44,27 @@ impl View for ContactNotification {
}
fn render(&mut self, cx: &mut RenderContext<Self>) -> 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<User>,
+ kind: client::ContactEventKind,
user_store: ModelHandle<UserStore>,
cx: &mut ViewContext<Self>,
) -> 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>) {
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);
@@ -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;
@@ -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<T> SidebarItemHandle for ViewHandle<T>
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<dyn SidebarItemHandle>,
- _observation: Subscription,
+ _subscriptions: [Subscription; 2],
}
pub struct SidebarButtons {
@@ -99,11 +107,24 @@ impl Sidebar {
view: ViewHandle<T>,
cx: &mut ViewContext<Self>,
) {
- 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()
}