mod participant; pub mod room; use anyhow::{anyhow, Result}; use client::{proto, Client, TypedEnvelope, User, UserStore}; use gpui::{ AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Subscription, Task, }; pub use participant::ParticipantLocation; use postage::watch; use project::Project; pub use room::Room; use std::sync::Arc; pub fn init(client: Arc, user_store: ModelHandle, cx: &mut MutableAppContext) { let active_call = cx.add_model(|cx| ActiveCall::new(client, user_store, cx)); cx.set_global(active_call); } #[derive(Clone)] pub struct IncomingCall { pub room_id: u64, pub caller: Arc, pub participants: Vec>, pub initial_project_id: Option, } pub struct ActiveCall { room: Option<(ModelHandle, Vec)>, incoming_call: ( watch::Sender>, watch::Receiver>, ), client: Arc, user_store: ModelHandle, _subscriptions: Vec, } impl Entity for ActiveCall { type Event = room::Event; } impl ActiveCall { fn new( client: Arc, user_store: ModelHandle, cx: &mut ModelContext, ) -> Self { Self { room: None, incoming_call: watch::channel(), _subscriptions: vec![ client.add_request_handler(cx.handle(), Self::handle_incoming_call), client.add_message_handler(cx.handle(), Self::handle_call_canceled), ], client, user_store, } } async fn handle_incoming_call( this: ModelHandle, envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, ) -> Result { let user_store = this.read_with(&cx, |this, _| this.user_store.clone()); let call = IncomingCall { room_id: envelope.payload.room_id, participants: user_store .update(&mut cx, |user_store, cx| { user_store.get_users(envelope.payload.participant_user_ids, cx) }) .await?, caller: user_store .update(&mut cx, |user_store, cx| { user_store.get_user(envelope.payload.caller_user_id, cx) }) .await?, initial_project_id: envelope.payload.initial_project_id, }; this.update(&mut cx, |this, _| { *this.incoming_call.0.borrow_mut() = Some(call); }); Ok(proto::Ack {}) } async fn handle_call_canceled( this: ModelHandle, _: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, ) -> Result<()> { this.update(&mut cx, |this, _| { *this.incoming_call.0.borrow_mut() = None; }); Ok(()) } pub fn global(cx: &AppContext) -> ModelHandle { cx.global::>().clone() } pub fn invite( &mut self, recipient_user_id: u64, initial_project: Option>, cx: &mut ModelContext, ) -> Task> { let room = self.room.as_ref().map(|(room, _)| room.clone()); let client = self.client.clone(); let user_store = self.user_store.clone(); cx.spawn(|this, mut cx| async move { let room = if let Some(room) = room { room } else { cx.update(|cx| Room::create(client, user_store, cx)).await? }; let initial_project_id = if let Some(initial_project) = initial_project { let room_id = room.read_with(&cx, |room, _| room.id()); Some( initial_project .update(&mut cx, |project, cx| project.share(room_id, cx)) .await?, ) } else { None }; this.update(&mut cx, |this, cx| this.set_room(Some(room.clone()), cx)); room.update(&mut cx, |room, cx| { room.call(recipient_user_id, initial_project_id, cx) }) .await?; Ok(()) }) } pub fn cancel_invite( &mut self, recipient_user_id: u64, cx: &mut ModelContext, ) -> Task> { let client = self.client.clone(); cx.foreground().spawn(async move { client .request(proto::CancelCall { recipient_user_id }) .await?; anyhow::Ok(()) }) } pub fn incoming(&self) -> watch::Receiver> { self.incoming_call.1.clone() } pub fn accept_incoming(&mut self, cx: &mut ModelContext) -> Task> { if self.room.is_some() { return Task::ready(Err(anyhow!("cannot join while on another call"))); } let call = if let Some(call) = self.incoming_call.1.borrow().clone() { call } else { return Task::ready(Err(anyhow!("no incoming call"))); }; let join = Room::join(&call, self.client.clone(), self.user_store.clone(), cx); cx.spawn(|this, mut cx| async move { let room = join.await?; this.update(&mut cx, |this, cx| this.set_room(Some(room.clone()), cx)); Ok(()) }) } pub fn decline_incoming(&mut self) -> Result<()> { *self.incoming_call.0.borrow_mut() = None; self.client.send(proto::DeclineCall {})?; Ok(()) } pub fn hang_up(&mut self, cx: &mut ModelContext) -> Result<()> { if let Some((room, _)) = self.room.take() { room.update(cx, |room, cx| room.leave(cx))?; } Ok(()) } fn set_room(&mut self, room: Option>, cx: &mut ModelContext) { if room.as_ref() != self.room.as_ref().map(|room| &room.0) { if let Some(room) = room { let subscriptions = vec![ cx.observe(&room, |_, _, cx| cx.notify()), cx.subscribe(&room, |_, _, event, cx| cx.emit(event.clone())), ]; self.room = Some((room, subscriptions)); } else { self.room = None; } cx.notify(); } } pub fn room(&self) -> Option<&ModelHandle> { self.room.as_ref().map(|(room, _)| room) } }