Merge pull request #1806 from zed-industries/pending-state-when-calling

Show a `Calling` indicator right away when initiating a call
This commit is contained in:
Antonio Scandurra 2022-10-25 10:10:44 +01:00 committed by GitHub
commit af74d5409a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 80 additions and 32 deletions

View file

@ -3,6 +3,7 @@ pub mod room;
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use client::{proto, Client, TypedEnvelope, User, UserStore}; use client::{proto, Client, TypedEnvelope, User, UserStore};
use collections::HashSet;
use gpui::{ use gpui::{
AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext,
Subscription, Task, WeakModelHandle, Subscription, Task, WeakModelHandle,
@ -27,8 +28,9 @@ pub struct IncomingCall {
} }
pub struct ActiveCall { pub struct ActiveCall {
location: Option<WeakModelHandle<Project>>,
room: Option<(ModelHandle<Room>, Vec<Subscription>)>, room: Option<(ModelHandle<Room>, Vec<Subscription>)>,
location: Option<WeakModelHandle<Project>>,
pending_invites: HashSet<u64>,
incoming_call: ( incoming_call: (
watch::Sender<Option<IncomingCall>>, watch::Sender<Option<IncomingCall>>,
watch::Receiver<Option<IncomingCall>>, watch::Receiver<Option<IncomingCall>>,
@ -51,6 +53,7 @@ impl ActiveCall {
Self { Self {
room: None, room: None,
location: None, location: None,
pending_invites: Default::default(),
incoming_call: watch::channel(), incoming_call: watch::channel(),
_subscriptions: vec![ _subscriptions: vec![
client.add_request_handler(cx.handle(), Self::handle_incoming_call), client.add_request_handler(cx.handle(), Self::handle_incoming_call),
@ -113,33 +116,49 @@ impl ActiveCall {
) -> Task<Result<()>> { ) -> Task<Result<()>> {
let client = self.client.clone(); let client = self.client.clone();
let user_store = self.user_store.clone(); let user_store = self.user_store.clone();
cx.spawn(|this, mut cx| async move { if !self.pending_invites.insert(recipient_user_id) {
if let Some(room) = this.read_with(&cx, |this, _| this.room().cloned()) { return Task::ready(Err(anyhow!("user was already invited")));
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| { cx.notify();
room.call(recipient_user_id, initial_project_id, cx) cx.spawn(|this, mut cx| async move {
}) let invite = async {
.await?; if let Some(room) = this.read_with(&cx, |this, _| this.room().cloned()) {
} else { let initial_project_id = if let Some(initial_project) = initial_project {
let room = cx Some(
.update(|cx| { room.update(&mut cx, |room, cx| {
Room::create(recipient_user_id, initial_project, client, user_store, 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?; .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)) this.update(&mut cx, |this, cx| this.set_room(Some(room), cx))
.await?; .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<Room>> { pub fn room(&self) -> Option<&ModelHandle<Room>> {
self.room.as_ref().map(|(room, _)| room) self.room.as_ref().map(|(room, _)| room)
} }
pub fn pending_invites(&self) -> &HashSet<u64> {
&self.pending_invites
}
} }

View file

@ -73,7 +73,10 @@ enum ContactEntry {
}, },
IncomingRequest(Arc<User>), IncomingRequest(Arc<User>),
OutgoingRequest(Arc<User>), OutgoingRequest(Arc<User>),
Contact(Arc<Contact>), Contact {
contact: Arc<Contact>,
calling: bool,
},
} }
impl PartialEq for ContactEntry { impl PartialEq for ContactEntry {
@ -121,8 +124,13 @@ impl PartialEq for ContactEntry {
return user_1.id == user_2.id; return user_1.id == user_2.id;
} }
} }
ContactEntry::Contact(contact_1) => { ContactEntry::Contact {
if let ContactEntry::Contact(contact_2) = other { contact: contact_1, ..
} => {
if let ContactEntry::Contact {
contact: contact_2, ..
} = other
{
return contact_1.user.id == contact_2.user.id; return contact_1.user.id == contact_2.user.id;
} }
} }
@ -255,8 +263,9 @@ impl ContactList {
is_selected, is_selected,
cx, cx,
), ),
ContactEntry::Contact(contact) => Self::render_contact( ContactEntry::Contact { contact, calling } => Self::render_contact(
contact, contact,
*calling,
&this.project, &this.project,
&theme.contact_list, &theme.contact_list,
is_selected, is_selected,
@ -349,8 +358,8 @@ impl ContactList {
let section = *section; let section = *section;
self.toggle_expanded(&ToggleExpanded(section), cx); self.toggle_expanded(&ToggleExpanded(section), cx);
} }
ContactEntry::Contact(contact) => { ContactEntry::Contact { contact, calling } => {
if contact.online && !contact.busy { if contact.online && !contact.busy && !calling {
self.call( self.call(
&Call { &Call {
recipient_user_id: contact.user.id, recipient_user_id: contact.user.id,
@ -621,9 +630,13 @@ impl ContactList {
if !matches.is_empty() { if !matches.is_empty() {
self.entries.push(ContactEntry::Header(section)); self.entries.push(ContactEntry::Header(section));
if !self.collapsed_sections.contains(&section) { if !self.collapsed_sections.contains(&section) {
let active_call = &ActiveCall::global(cx).read(cx);
for mat in matches { for mat in matches {
let contact = &contacts[mat.candidate_id]; 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( fn render_contact(
contact: &Contact, contact: &Contact,
calling: bool,
project: &ModelHandle<Project>, project: &ModelHandle<Project>,
theme: &theme::ContactList, theme: &theme::ContactList,
is_selected: bool, is_selected: bool,
cx: &mut RenderContext<Self>, cx: &mut RenderContext<Self>,
) -> ElementBox { ) -> ElementBox {
let online = contact.online; let online = contact.online;
let busy = contact.busy; let busy = contact.busy || calling;
let user_id = contact.user.id; let user_id = contact.user.id;
let initial_project = project.clone(); let initial_project = project.clone();
let mut element = let mut element =
@ -986,7 +1000,7 @@ impl ContactList {
Empty::new() Empty::new()
.collapsed() .collapsed()
.contained() .contained()
.with_style(if contact.busy { .with_style(if busy {
theme.contact_status_busy theme.contact_status_busy
} else { } else {
theme.contact_status_free theme.contact_status_free
@ -1020,6 +1034,17 @@ impl ContactList {
.flex(1., true) .flex(1., true)
.boxed(), .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() .constrained()
.with_height(theme.row_height) .with_height(theme.row_height)
.contained() .contained()
@ -1164,7 +1189,7 @@ impl ContactList {
let initial_project = action.initial_project.clone(); let initial_project = action.initial_project.clone();
ActiveCall::global(cx) ActiveCall::global(cx)
.update(cx, |call, 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); .detach_and_log_err(cx);
} }