From 5d20338f69b44830770848476ae29b870fdddb64 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sat, 7 May 2022 15:09:27 -0600 Subject: [PATCH] Get basic test of accepting a contact request passing --- crates/client/src/user.rs | 24 +++++--- crates/collab/src/db.rs | 12 ++-- crates/collab/src/rpc.rs | 105 ++++++++++++++++++++++++++++++--- crates/collab/src/rpc/store.rs | 2 +- crates/rpc/proto/zed.proto | 6 +- 5 files changed, 125 insertions(+), 24 deletions(-) diff --git a/crates/client/src/user.rs b/crates/client/src/user.rs index d98f813dbe..013d11d4dd 100644 --- a/crates/client/src/user.rs +++ b/crates/client/src/user.rs @@ -125,7 +125,7 @@ impl UserStore { user_ids.insert(contact.user_id); user_ids.extend(contact.projects.iter().flat_map(|w| &w.guests).copied()); } - user_ids.extend(message.incoming_requests.iter().map(|req| req.user_id)); + user_ids.extend(message.incoming_requests.iter().map(|req| req.requester_id)); user_ids.extend(message.outgoing_requests.iter()); let load_users = self.get_users(user_ids.into_iter().collect(), cx); @@ -144,8 +144,10 @@ impl UserStore { let mut incoming_requests = Vec::new(); for request in message.incoming_requests { incoming_requests.push( - this.update(&mut cx, |this, cx| this.fetch_user(request.user_id, cx)) - .await?, + this.update(&mut cx, |this, cx| { + this.fetch_user(request.requester_id, cx) + }) + .await?, ); } @@ -199,12 +201,20 @@ impl UserStore { .is_ok() } - pub fn request_contact(&self, to_user_id: u64) -> impl Future> { + pub fn incoming_contact_requests(&self) -> &[Arc] { + &self.incoming_contact_requests + } + + pub fn outgoing_contact_requests(&self) -> &[Arc] { + &self.outgoing_contact_requests + } + + pub fn request_contact(&self, responder_id: u64) -> impl Future> { let client = self.client.upgrade(); async move { client .ok_or_else(|| anyhow!("not logged in"))? - .request(proto::RequestContact { to_user_id }) + .request(proto::RequestContact { responder_id }) .await?; Ok(()) } @@ -212,7 +222,7 @@ impl UserStore { pub fn respond_to_contact_request( &self, - from_user_id: u64, + requester_id: u64, accept: bool, ) -> impl Future> { let client = self.client.upgrade(); @@ -220,7 +230,7 @@ impl UserStore { client .ok_or_else(|| anyhow!("not logged in"))? .request(proto::RespondToContactRequest { - requesting_user_id: from_user_id, + requester_id, response: if accept { proto::ContactRequestResponse::Accept } else { diff --git a/crates/collab/src/db.rs b/crates/collab/src/db.rs index 6d65fd307d..8a13d4fef4 100644 --- a/crates/collab/src/db.rs +++ b/crates/collab/src/db.rs @@ -211,7 +211,7 @@ impl Db for PostgresDb { outgoing_requests.push(user_id_b); } else { incoming_requests.push(IncomingContactRequest { - requesting_user_id: user_id_b, + requester_id: user_id_b, should_notify, }); } @@ -220,7 +220,7 @@ impl Db for PostgresDb { current.push(user_id_a); } else if a_to_b { incoming_requests.push(IncomingContactRequest { - requesting_user_id: user_id_a, + requester_id: user_id_a, should_notify, }); } else { @@ -675,7 +675,7 @@ pub struct Contacts { #[derive(Clone, Debug, PartialEq, Eq)] pub struct IncomingContactRequest { - pub requesting_user_id: UserId, + pub requester_id: UserId, pub should_notify: bool, } @@ -935,7 +935,7 @@ pub mod tests { current: vec![], outgoing_requests: vec![], incoming_requests: vec![IncomingContactRequest { - requesting_user_id: user_1, + requester_id: user_1, should_notify: true }], }, @@ -953,7 +953,7 @@ pub mod tests { current: vec![], outgoing_requests: vec![], incoming_requests: vec![IncomingContactRequest { - requesting_user_id: user_1, + requester_id: user_1, should_notify: false }], }, @@ -1195,7 +1195,7 @@ pub mod tests { current.push(contact.requester_id); } else { incoming_requests.push(IncomingContactRequest { - requesting_user_id: contact.requester_id, + requester_id: contact.requester_id, should_notify: contact.should_notify, }); } diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 6c345ce318..a9639d82f2 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -276,7 +276,7 @@ impl Server { store.add_connection(connection_id, user_id); let update_contacts = store.build_initial_contacts_update(contacts); for connection_id in store.connection_ids_for_user(user_id) { - this.peer.send(connection_id, update_contacts.clone()); + this.peer.send(connection_id, update_contacts.clone())?; } } @@ -953,11 +953,30 @@ impl Server { .read() .await .user_id_for_connection(request.sender_id)?; - let responder_id = UserId::from_proto(request.payload.to_user_id); + let responder_id = UserId::from_proto(request.payload.responder_id); self.app_state .db .send_contact_request(requester_id, responder_id) .await?; + + + // Update outgoing contact requests of requester + let mut update = proto::UpdateContacts::default(); + update.outgoing_requests.push(responder_id.to_proto()); + for connection_id in self.store().await.connection_ids_for_user(requester_id) { + self.peer.send(connection_id, update.clone())?; + } + + // Update incoming contact requests of responder + let mut update = proto::UpdateContacts::default(); + update.incoming_requests.push(proto::IncomingContactRequest { + requester_id: requester_id.to_proto(), + should_notify: true, + }); + for connection_id in self.store().await.connection_ids_for_user(responder_id) { + self.peer.send(connection_id, update.clone())?; + } + response.send(proto::Ack {})?; Ok(()) } @@ -972,15 +991,45 @@ impl Server { .read() .await .user_id_for_connection(request.sender_id)?; - let requester_id = UserId::from_proto(request.payload.requesting_user_id); + let requester_id = UserId::from_proto(request.payload.requester_id); + let accept = request.payload.response == proto::ContactRequestResponse::Accept as i32; self.app_state .db .respond_to_contact_request( responder_id, requester_id, - request.payload.response == proto::ContactRequestResponse::Accept as i32, + accept, ) .await?; + + if accept { + // Update responder with new contact + let mut update = proto::UpdateContacts::default(); + update.contacts.push(proto::Contact { + user_id: requester_id.to_proto(), + projects: Default::default(), // TODO + online: true, // TODO + }); + update.remove_incoming_requests.push(requester_id.to_proto()); + for connection_id in self.store.read().await.connection_ids_for_user(responder_id) { + self.peer.send(connection_id, update.clone())?; + } + + // Update requester with new contact + let mut update = proto::UpdateContacts::default(); + update.contacts.push(proto::Contact { + user_id: responder_id.to_proto(), + projects: Default::default(), // TODO + online: true, // TODO + }); + update.remove_outgoing_requests.push(responder_id.to_proto()); + for connection_id in self.store.read().await.connection_ids_for_user(requester_id) { + self.peer.send(connection_id, update.clone())?; + } + } else { + todo!() + } + response.send(proto::Ack {})?; Ok(()) } @@ -4987,8 +5036,8 @@ mod tests { } } - #[gpui::test(iterations = 10)] - async fn test_contacts_requests(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { + #[gpui::test(iterations = 1)] // TODO: More iterations + async fn test_contacts_requests(executor: Arc, cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { cx_a.foreground().forbid_parking(); // Connect to a server as 3 clients. @@ -4996,6 +5045,8 @@ mod tests { let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; + // User A requests that user B become their contact + client_a .user_store .read_with(cx_a, |store, _| { @@ -5003,15 +5054,55 @@ mod tests { }) .await .unwrap(); + + executor.run_until_parked(); + + // Both parties see the pending request appear. User B accepts the request. + + client_a.user_store.read_with(cx_a, |store, _| { + let contacts = store + .outgoing_contact_requests() + .iter() + .map(|contact| contact.github_login.clone()) + .collect::>(); + assert_eq!(contacts, &["user_b"]); + }); + + client_b.user_store.read_with(cx_b, |store, _| { + let contacts = store + .incoming_contact_requests() + .iter() + .map(|contact| contact.github_login.clone()) + .collect::>(); + assert_eq!(contacts, &["user_a"]); + + store.respond_to_contact_request(client_a.user_id().unwrap(), true) + }).await.unwrap(); + executor.run_until_parked(); + + // User B sees user A as their contact now, and the incoming request from them is removed + client_b.user_store.read_with(cx_b, |store, _| { + let contacts = store + .contacts() + .iter() + .map(|contact| contact.user.github_login.clone()) + .collect::>(); + assert_eq!(contacts, &["user_a"]); + assert!(store.incoming_contact_requests().is_empty()); + }); + + // User A sees user B as their contact now, and the outgoing request to them is removed client_a.user_store.read_with(cx_a, |store, _| { let contacts = store .contacts() .iter() .map(|contact| contact.user.github_login.clone()) .collect::>(); - assert_eq!(contacts, &["user_b"]) + assert_eq!(contacts, &["user_b"]); + assert!(store.outgoing_contact_requests().is_empty()); }); + } #[gpui::test(iterations = 10)] diff --git a/crates/collab/src/rpc/store.rs b/crates/collab/src/rpc/store.rs index 3ae9e09916..a115db762a 100644 --- a/crates/collab/src/rpc/store.rs +++ b/crates/collab/src/rpc/store.rs @@ -225,7 +225,7 @@ impl Store { update .incoming_requests .push(proto::IncomingContactRequest { - user_id: request.requesting_user_id.to_proto(), + requester_id: request.requester_id.to_proto(), should_notify: request.should_notify, }) } diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index 5716be9d15..97e0930f4f 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -550,11 +550,11 @@ message UsersResponse { } message RequestContact { - uint64 to_user_id = 1; + uint64 responder_id = 1; } message RespondToContactRequest { - uint64 requesting_user_id = 1; + uint64 requester_id = 1; ContactRequestResponse response = 2; } @@ -599,7 +599,7 @@ message UpdateContacts { } message IncomingContactRequest { - uint64 user_id = 1; + uint64 requester_id = 1; bool should_notify = 2; }