From 2a3773240d4873fa564d31a1c34889058344d35a Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 25 Oct 2022 09:58:22 +0200 Subject: [PATCH] Show a `Calling` indicator right away when initiating a call --- crates/call/src/call.rs | 67 +++++++++++++++++++--------- crates/collab_ui/src/contact_list.rs | 45 ++++++++++++++----- 2 files changed, 80 insertions(+), 32 deletions(-) diff --git a/crates/call/src/call.rs b/crates/call/src/call.rs index 106006007c..6b72eb61da 100644 --- a/crates/call/src/call.rs +++ b/crates/call/src/call.rs @@ -3,6 +3,7 @@ pub mod room; use anyhow::{anyhow, Result}; use client::{proto, Client, TypedEnvelope, User, UserStore}; +use collections::HashSet; use gpui::{ AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Subscription, Task, WeakModelHandle, @@ -27,8 +28,9 @@ pub struct IncomingCall { } pub struct ActiveCall { - location: Option>, room: Option<(ModelHandle, Vec)>, + location: Option>, + pending_invites: HashSet, incoming_call: ( watch::Sender>, watch::Receiver>, @@ -51,6 +53,7 @@ impl ActiveCall { Self { room: None, location: None, + pending_invites: Default::default(), incoming_call: watch::channel(), _subscriptions: vec![ client.add_request_handler(cx.handle(), Self::handle_incoming_call), @@ -113,33 +116,49 @@ impl ActiveCall { ) -> Task> { let client = self.client.clone(); let user_store = self.user_store.clone(); - cx.spawn(|this, mut cx| async move { - if let Some(room) = this.read_with(&cx, |this, _| this.room().cloned()) { - let initial_project_id = if let Some(initial_project) = initial_project { - Some( - room.update(&mut cx, |room, cx| room.share_project(initial_project, cx)) - .await?, - ) - } else { - None - }; + if !self.pending_invites.insert(recipient_user_id) { + return Task::ready(Err(anyhow!("user was already invited"))); + } - room.update(&mut cx, |room, cx| { - room.call(recipient_user_id, initial_project_id, cx) - }) - .await?; - } else { - let room = cx - .update(|cx| { - Room::create(recipient_user_id, initial_project, client, user_store, cx) + cx.notify(); + cx.spawn(|this, mut cx| async move { + let invite = async { + if let Some(room) = this.read_with(&cx, |this, _| this.room().cloned()) { + let initial_project_id = if let Some(initial_project) = initial_project { + Some( + room.update(&mut cx, |room, cx| { + room.share_project(initial_project, cx) + }) + .await?, + ) + } else { + None + }; + + room.update(&mut cx, |room, cx| { + room.call(recipient_user_id, initial_project_id, cx) }) .await?; + } else { + let room = cx + .update(|cx| { + Room::create(recipient_user_id, initial_project, client, user_store, cx) + }) + .await?; - this.update(&mut cx, |this, cx| this.set_room(Some(room.clone()), cx)) - .await?; + this.update(&mut cx, |this, cx| this.set_room(Some(room), cx)) + .await?; + }; + + Ok(()) }; - Ok(()) + let result = invite.await; + this.update(&mut cx, |this, cx| { + this.pending_invites.remove(&recipient_user_id); + cx.notify(); + }); + result }) } @@ -274,4 +293,8 @@ impl ActiveCall { pub fn room(&self) -> Option<&ModelHandle> { self.room.as_ref().map(|(room, _)| room) } + + pub fn pending_invites(&self) -> &HashSet { + &self.pending_invites + } } diff --git a/crates/collab_ui/src/contact_list.rs b/crates/collab_ui/src/contact_list.rs index 1947d14a16..9091e2687b 100644 --- a/crates/collab_ui/src/contact_list.rs +++ b/crates/collab_ui/src/contact_list.rs @@ -73,7 +73,10 @@ enum ContactEntry { }, IncomingRequest(Arc), OutgoingRequest(Arc), - Contact(Arc), + Contact { + contact: Arc, + calling: bool, + }, } impl PartialEq for ContactEntry { @@ -121,8 +124,13 @@ impl PartialEq for ContactEntry { return user_1.id == user_2.id; } } - ContactEntry::Contact(contact_1) => { - if let ContactEntry::Contact(contact_2) = other { + ContactEntry::Contact { + contact: contact_1, .. + } => { + if let ContactEntry::Contact { + contact: contact_2, .. + } = other + { return contact_1.user.id == contact_2.user.id; } } @@ -255,8 +263,9 @@ impl ContactList { is_selected, cx, ), - ContactEntry::Contact(contact) => Self::render_contact( + ContactEntry::Contact { contact, calling } => Self::render_contact( contact, + *calling, &this.project, &theme.contact_list, is_selected, @@ -349,8 +358,8 @@ impl ContactList { let section = *section; self.toggle_expanded(&ToggleExpanded(section), cx); } - ContactEntry::Contact(contact) => { - if contact.online && !contact.busy { + ContactEntry::Contact { contact, calling } => { + if contact.online && !contact.busy && !calling { self.call( &Call { recipient_user_id: contact.user.id, @@ -621,9 +630,13 @@ impl ContactList { if !matches.is_empty() { self.entries.push(ContactEntry::Header(section)); if !self.collapsed_sections.contains(§ion) { + let active_call = &ActiveCall::global(cx).read(cx); for mat in matches { let contact = &contacts[mat.candidate_id]; - self.entries.push(ContactEntry::Contact(contact.clone())); + self.entries.push(ContactEntry::Contact { + contact: contact.clone(), + calling: active_call.pending_invites().contains(&contact.user.id), + }); } } } @@ -968,13 +981,14 @@ impl ContactList { fn render_contact( contact: &Contact, + calling: bool, project: &ModelHandle, theme: &theme::ContactList, is_selected: bool, cx: &mut RenderContext, ) -> ElementBox { let online = contact.online; - let busy = contact.busy; + let busy = contact.busy || calling; let user_id = contact.user.id; let initial_project = project.clone(); let mut element = @@ -986,7 +1000,7 @@ impl ContactList { Empty::new() .collapsed() .contained() - .with_style(if contact.busy { + .with_style(if busy { theme.contact_status_busy } else { theme.contact_status_free @@ -1020,6 +1034,17 @@ impl ContactList { .flex(1., true) .boxed(), ) + .with_children(if calling { + Some( + Label::new("Calling".to_string(), theme.calling_indicator.text.clone()) + .contained() + .with_style(theme.calling_indicator.container) + .aligned() + .boxed(), + ) + } else { + None + }) .constrained() .with_height(theme.row_height) .contained() @@ -1164,7 +1189,7 @@ impl ContactList { let initial_project = action.initial_project.clone(); ActiveCall::global(cx) .update(cx, |call, cx| { - call.invite(recipient_user_id, initial_project.clone(), cx) + call.invite(recipient_user_id, initial_project, cx) }) .detach_and_log_err(cx); }