crates/call/src/call.rs 🔗
@@ -1,5 +1,5 @@
mod participant;
-mod room;
+pub mod room;
use anyhow::{anyhow, Result};
use client::{incoming_call::IncomingCall, Client, UserStore};
Antonio Scandurra created
crates/call/src/call.rs | 2
crates/call/src/room.rs | 40 ++++++++++-
crates/client/src/user.rs | 2
crates/collab/src/integration_tests.rs | 87 +++++++++++++++++++++++++++
4 files changed, 120 insertions(+), 11 deletions(-)
@@ -1,5 +1,5 @@
mod participant;
-mod room;
+pub mod room;
use anyhow::{anyhow, Result};
use client::{incoming_call::IncomingCall, Client, UserStore};
@@ -1,14 +1,15 @@
use crate::participant::{ParticipantLocation, RemoteParticipant};
use anyhow::{anyhow, Result};
use client::{incoming_call::IncomingCall, proto, Client, PeerId, TypedEnvelope, User, UserStore};
-use collections::HashMap;
+use collections::{HashMap, HashSet};
use futures::StreamExt;
use gpui::{AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task};
use std::sync::Arc;
use util::ResultExt;
+#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Event {
- PeerChangedActiveProject,
+ RemoteProjectShared { owner: Arc<User>, project_id: u64 },
}
pub struct Room {
@@ -158,19 +159,46 @@ impl Room {
this.update(&mut cx, |this, cx| {
if let Some(participants) = participants.log_err() {
- // TODO: compute diff instead of clearing participants
- this.remote_participants.clear();
+ let mut seen_participants = HashSet::default();
+
for (participant, user) in room.participants.into_iter().zip(participants) {
+ let peer_id = PeerId(participant.peer_id);
+ seen_participants.insert(peer_id);
+
+ let existing_project_ids = this
+ .remote_participants
+ .get(&peer_id)
+ .map(|existing| existing.project_ids.clone())
+ .unwrap_or_default();
+ for project_id in &participant.project_ids {
+ if !existing_project_ids.contains(project_id) {
+ cx.emit(Event::RemoteProjectShared {
+ owner: user.clone(),
+ project_id: *project_id,
+ });
+ }
+ }
+
this.remote_participants.insert(
- PeerId(participant.peer_id),
+ peer_id,
RemoteParticipant {
- user,
+ user: user.clone(),
project_ids: participant.project_ids,
location: ParticipantLocation::from_proto(participant.location)
.unwrap_or(ParticipantLocation::External),
},
);
}
+
+ for participant_peer_id in
+ this.remote_participants.keys().copied().collect::<Vec<_>>()
+ {
+ if !seen_participants.contains(&participant_peer_id) {
+ this.remote_participants.remove(&participant_peer_id);
+ }
+ }
+
+ cx.notify();
}
if let Some(pending_users) = pending_users.log_err() {
@@ -9,7 +9,7 @@ use rpc::proto::{RequestMessage, UsersResponse};
use std::sync::{Arc, Weak};
use util::TryFutureExt as _;
-#[derive(Debug)]
+#[derive(Default, Debug)]
pub struct User {
pub id: u64,
pub github_login: String,
@@ -5,10 +5,10 @@ use crate::{
};
use ::rpc::Peer;
use anyhow::anyhow;
-use call::Room;
+use call::{room, Room};
use client::{
self, test::FakeHttpClient, Channel, ChannelDetails, ChannelList, Client, Connection,
- Credentials, EstablishConnectionError, UserStore, RECEIVE_TIMEOUT,
+ Credentials, EstablishConnectionError, User, UserStore, RECEIVE_TIMEOUT,
};
use collections::{BTreeMap, HashMap, HashSet};
use editor::{
@@ -40,7 +40,8 @@ use serde_json::json;
use settings::{Formatter, Settings};
use sqlx::types::time::OffsetDateTime;
use std::{
- env,
+ cell::RefCell,
+ env, mem,
ops::Deref,
path::{Path, PathBuf},
rc::Rc,
@@ -556,6 +557,86 @@ async fn test_host_disconnect(
});
}
+#[gpui::test(iterations = 10)]
+async fn test_room_events(
+ deterministic: Arc<Deterministic>,
+ cx_a: &mut TestAppContext,
+ cx_b: &mut TestAppContext,
+) {
+ deterministic.forbid_parking();
+ let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
+ let client_a = server.create_client(cx_a, "user_a").await;
+ let client_b = server.create_client(cx_b, "user_b").await;
+ client_a.fs.insert_tree("/a", json!({})).await;
+ client_b.fs.insert_tree("/b", json!({})).await;
+
+ let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
+ let (project_b, _) = client_b.build_local_project("/b", cx_b).await;
+
+ let (room_id, mut rooms) = server
+ .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)])
+ .await;
+
+ let room_a = rooms.remove(0);
+ let room_a_events = room_events(&room_a, cx_a);
+
+ let room_b = rooms.remove(0);
+ let room_b_events = room_events(&room_b, cx_b);
+
+ let project_a_id = project_a
+ .update(cx_a, |project, cx| project.share(room_id, cx))
+ .await
+ .unwrap();
+ deterministic.run_until_parked();
+ assert_eq!(mem::take(&mut *room_a_events.borrow_mut()), vec![]);
+ assert_eq!(
+ mem::take(&mut *room_b_events.borrow_mut()),
+ vec![room::Event::RemoteProjectShared {
+ owner: Arc::new(User {
+ id: client_a.user_id().unwrap(),
+ github_login: "user_a".to_string(),
+ avatar: None,
+ }),
+ project_id: project_a_id,
+ }]
+ );
+
+ let project_b_id = project_b
+ .update(cx_b, |project, cx| project.share(room_id, cx))
+ .await
+ .unwrap();
+ deterministic.run_until_parked();
+ assert_eq!(
+ mem::take(&mut *room_a_events.borrow_mut()),
+ vec![room::Event::RemoteProjectShared {
+ owner: Arc::new(User {
+ id: client_b.user_id().unwrap(),
+ github_login: "user_b".to_string(),
+ avatar: None,
+ }),
+ project_id: project_b_id,
+ }]
+ );
+ assert_eq!(mem::take(&mut *room_b_events.borrow_mut()), vec![]);
+
+ fn room_events(
+ room: &ModelHandle<Room>,
+ cx: &mut TestAppContext,
+ ) -> Rc<RefCell<Vec<room::Event>>> {
+ let events = Rc::new(RefCell::new(Vec::new()));
+ cx.update({
+ let events = events.clone();
+ |cx| {
+ cx.subscribe(room, move |_, event, _| {
+ events.borrow_mut().push(event.clone())
+ })
+ .detach()
+ }
+ });
+ events
+ }
+}
+
#[gpui::test(iterations = 10)]
async fn test_propagate_saves_and_fs_changes(
cx_a: &mut TestAppContext,