Send a `ShowContacts` message the first time a user connects to collab

Antonio Scandurra created

Change summary

crates/collab/migrations/20220518151305_add_invites_to_users.sql |  1 
crates/collab/src/db.rs                                          | 27 ++
crates/collab/src/rpc.rs                                         |  5 
crates/rpc/proto/zed.proto                                       | 27 +
crates/rpc/src/proto.rs                                          |  1 
5 files changed, 49 insertions(+), 12 deletions(-)

Detailed changes

crates/collab/migrations/20220518151305_add_invites_to_users.sql 🔗

@@ -2,6 +2,7 @@ ALTER TABLE users
 ADD invite_code VARCHAR(64),
 ADD invite_count INTEGER NOT NULL DEFAULT 0,
 ADD inviter_id INTEGER REFERENCES users (id),
+ADD first_connection BOOLEAN NOT NULL DEFAULT true,
 ADD created_at TIMESTAMP NOT NULL DEFAULT NOW();
 
 CREATE UNIQUE INDEX "index_invite_code_users" ON "users" ("invite_code");

crates/collab/src/db.rs 🔗

@@ -18,6 +18,7 @@ pub trait Db: Send + Sync {
     async fn get_users_by_ids(&self, ids: Vec<UserId>) -> Result<Vec<User>>;
     async fn get_user_by_github_login(&self, github_login: &str) -> Result<Option<User>>;
     async fn set_user_is_admin(&self, id: UserId, is_admin: bool) -> Result<()>;
+    async fn set_user_first_connection(&self, id: UserId, first_connection: bool) -> Result<()>;
     async fn destroy_user(&self, id: UserId) -> Result<()>;
 
     async fn set_invite_count(&self, id: UserId, count: u32) -> Result<()>;
@@ -182,6 +183,16 @@ impl Db for PostgresDb {
             .map(drop)?)
     }
 
+    async fn set_user_first_connection(&self, id: UserId, first_connection: bool) -> Result<()> {
+        let query = "UPDATE users SET first_connection = $1 WHERE id = $2";
+        Ok(sqlx::query(query)
+            .bind(first_connection)
+            .bind(id.0)
+            .execute(&self.pool)
+            .await
+            .map(drop)?)
+    }
+
     async fn destroy_user(&self, id: UserId) -> Result<()> {
         let query = "DELETE FROM access_tokens WHERE user_id = $1;";
         sqlx::query(query)
@@ -847,6 +858,7 @@ pub struct User {
     pub admin: bool,
     pub invite_code: Option<String>,
     pub invite_count: i32,
+    pub first_connection: bool,
 }
 
 id_type!(OrgId);
@@ -1601,6 +1613,7 @@ pub mod tests {
                         admin,
                         invite_code: None,
                         invite_count: 0,
+                        first_connection: true,
                     },
                 );
                 Ok(user_id)
@@ -1638,6 +1651,20 @@ pub mod tests {
             unimplemented!()
         }
 
+        async fn set_user_first_connection(
+            &self,
+            id: UserId,
+            first_connection: bool,
+        ) -> Result<()> {
+            self.background.simulate_random_delay().await;
+            let mut users = self.users.lock();
+            let mut user = users
+                .get_mut(&id)
+                .ok_or_else(|| anyhow!("user not found"))?;
+            user.first_connection = first_connection;
+            Ok(())
+        }
+
         async fn destroy_user(&self, _id: UserId) -> Result<()> {
             unimplemented!()
         }

crates/collab/src/rpc.rs 🔗

@@ -280,6 +280,11 @@ impl Server {
                 let _ = send_connection_id.send(connection_id).await;
             }
 
+            if user.first_connection {
+                this.peer.send(connection_id, proto::ShowContacts {})?;
+                this.app_state.db.set_user_first_connection(user_id, false).await?;
+            }
+
             let (contacts, invite_code) = future::try_join(
                 this.app_state.db.get_contacts(user_id),
                 this.app_state.db.get_invite_code_for_user(user_id)

crates/rpc/proto/zed.proto 🔗

@@ -88,18 +88,19 @@ message Envelope {
 
         UpdateContacts update_contacts = 76;
         UpdateInviteInfo update_invite_info = 77;
-
-        GetUsers get_users = 78;
-        FuzzySearchUsers fuzzy_search_users = 79;
-        UsersResponse users_response = 80;
-        RequestContact request_contact = 81;
-        RespondToContactRequest respond_to_contact_request = 82;
-        RemoveContact remove_contact = 83;
-
-        Follow follow = 84;
-        FollowResponse follow_response = 85;
-        UpdateFollowers update_followers = 86;
-        Unfollow unfollow = 87;
+        ShowContacts show_contacts = 78;
+
+        GetUsers get_users = 79;
+        FuzzySearchUsers fuzzy_search_users = 80;
+        UsersResponse users_response = 81;
+        RequestContact request_contact = 82;
+        RespondToContactRequest respond_to_contact_request = 83;
+        RemoveContact remove_contact = 84;
+
+        Follow follow = 85;
+        FollowResponse follow_response = 86;
+        UpdateFollowers update_followers = 87;
+        Unfollow unfollow = 88;
     }
 }
 
@@ -640,6 +641,8 @@ message UpdateInviteInfo {
     uint32 count = 2;
 }
 
+message ShowContacts {}
+
 message IncomingContactRequest {
     uint64 requester_id = 1;
     bool should_notify = 2;

crates/rpc/src/proto.rs 🔗

@@ -145,6 +145,7 @@ messages!(
     (SearchProjectResponse, Background),
     (SendChannelMessage, Foreground),
     (SendChannelMessageResponse, Foreground),
+    (ShowContacts, Foreground),
     (StartLanguageServer, Foreground),
     (Test, Foreground),
     (Unfollow, Foreground),