diff --git a/crates/client/src/channel_store.rs b/crates/client/src/channel_store.rs new file mode 100644 index 0000000000..a72a189415 --- /dev/null +++ b/crates/client/src/channel_store.rs @@ -0,0 +1,189 @@ +use crate::{Client, Subscription, User, UserStore}; +use anyhow::Result; +use futures::Future; +use gpui::{AsyncAppContext, Entity, ModelContext, ModelHandle, Task}; +use rpc::{proto, TypedEnvelope}; +use std::sync::Arc; + +pub struct ChannelStore { + channels: Vec, + channel_invitations: Vec, + client: Arc, + user_store: ModelHandle, + rpc_subscription: Subscription, +} + +#[derive(Debug, PartialEq)] +pub struct Channel { + pub id: u64, + pub name: String, + pub parent_id: Option, +} + +impl Entity for ChannelStore { + type Event = (); +} + +impl ChannelStore { + pub fn new( + client: Arc, + user_store: ModelHandle, + cx: &mut ModelContext, + ) -> Self { + let rpc_subscription = + client.add_message_handler(cx.handle(), Self::handle_update_channels); + Self { + channels: vec![], + channel_invitations: vec![], + client, + user_store, + rpc_subscription, + } + } + + pub fn channels(&self) -> &[Channel] { + &self.channels + } + + pub fn channel_invitations(&self) -> &[Channel] { + &self.channel_invitations + } + + pub fn create_channel( + &self, + name: &str, + parent_id: Option, + ) -> impl Future> { + let client = self.client.clone(); + let name = name.to_owned(); + async move { + Ok(client + .request(proto::CreateChannel { name, parent_id }) + .await? + .channel_id) + } + } + + pub fn invite_member( + &self, + channel_id: u64, + user_id: u64, + admin: bool, + ) -> impl Future> { + let client = self.client.clone(); + async move { + client + .request(proto::InviteChannelMember { + channel_id, + user_id, + admin, + }) + .await?; + Ok(()) + } + } + + pub fn respond_to_channel_invite( + &mut self, + channel_id: u64, + accept: bool, + ) -> impl Future> { + let client = self.client.clone(); + async move { + client + .request(proto::RespondToChannelInvite { channel_id, accept }) + .await?; + Ok(()) + } + } + + pub fn remove_member( + &self, + channel_id: u64, + user_id: u64, + cx: &mut ModelContext, + ) -> Task> { + todo!() + } + + pub fn channel_members( + &self, + channel_id: u64, + cx: &mut ModelContext, + ) -> Task>>> { + todo!() + } + + pub fn add_guest_channel(&self, channel_id: u64) -> Task> { + todo!() + } + + async fn handle_update_channels( + this: ModelHandle, + message: TypedEnvelope, + _: Arc, + mut cx: AsyncAppContext, + ) -> Result<()> { + let payload = message.payload; + this.update(&mut cx, |this, cx| { + this.channels + .retain(|channel| !payload.remove_channels.contains(&channel.id)); + this.channel_invitations + .retain(|channel| !payload.remove_channel_invitations.contains(&channel.id)); + + for channel in payload.channel_invitations { + if let Some(existing_channel) = this + .channel_invitations + .iter_mut() + .find(|c| c.id == channel.id) + { + existing_channel.name = channel.name; + continue; + } + + this.channel_invitations.insert( + 0, + Channel { + id: channel.id, + name: channel.name, + parent_id: None, + }, + ); + } + + for channel in payload.channels { + if let Some(existing_channel) = + this.channels.iter_mut().find(|c| c.id == channel.id) + { + existing_channel.name = channel.name; + continue; + } + + if let Some(parent_id) = channel.parent_id { + if let Some(ix) = this.channels.iter().position(|c| c.id == parent_id) { + this.channels.insert( + ix + 1, + Channel { + id: channel.id, + name: channel.name, + parent_id: Some(parent_id), + }, + ); + } + } else { + this.channels.insert( + 0, + Channel { + id: channel.id, + name: channel.name, + parent_id: None, + }, + ); + } + } + cx.notify(); + }); + + Ok(()) + } +} diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index 78bcc55e93..af33c738ce 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -1,6 +1,7 @@ #[cfg(any(test, feature = "test-support"))] pub mod test; +pub mod channel_store; pub mod telemetry; pub mod user; @@ -44,6 +45,7 @@ use util::channel::ReleaseChannel; use util::http::HttpClient; use util::{ResultExt, TryFutureExt}; +pub use channel_store::*; pub use rpc::*; pub use telemetry::ClickhouseEvent; pub use user::*; diff --git a/crates/collab/migrations.sqlite/20221109000000_test_schema.sql b/crates/collab/migrations.sqlite/20221109000000_test_schema.sql index b397438e27..1ead36fde2 100644 --- a/crates/collab/migrations.sqlite/20221109000000_test_schema.sql +++ b/crates/collab/migrations.sqlite/20221109000000_test_schema.sql @@ -203,6 +203,7 @@ CREATE TABLE "channel_members" ( "channel_id" INTEGER NOT NULL REFERENCES channels (id) ON DELETE CASCADE, "user_id" INTEGER NOT NULL REFERENCES users (id) ON DELETE CASCADE, "admin" BOOLEAN NOT NULL DEFAULT false, + "accepted" BOOLEAN NOT NULL DEFAULT false, "updated_at" TIMESTAMP NOT NULL DEFAULT now ); diff --git a/crates/collab/migrations/20230727150500_add_channels.sql b/crates/collab/migrations/20230727150500_add_channels.sql index 0073d29c68..0588677792 100644 --- a/crates/collab/migrations/20230727150500_add_channels.sql +++ b/crates/collab/migrations/20230727150500_add_channels.sql @@ -22,6 +22,7 @@ CREATE TABLE "channel_members" ( "channel_id" INTEGER NOT NULL REFERENCES channels (id) ON DELETE CASCADE, "user_id" INTEGER NOT NULL REFERENCES users (id) ON DELETE CASCADE, "admin" BOOLEAN NOT NULL DEFAULT false, + "accepted" BOOLEAN NOT NULL DEFAULT false, "updated_at" TIMESTAMP NOT NULL DEFAULT now() ); diff --git a/crates/collab/src/db.rs b/crates/collab/src/db.rs index d3336824e6..46fca04658 100644 --- a/crates/collab/src/db.rs +++ b/crates/collab/src/db.rs @@ -3032,11 +3032,16 @@ impl Database { // channels - pub async fn create_root_channel(&self, name: &str) -> Result { - self.create_channel(name, None).await + pub async fn create_root_channel(&self, name: &str, creator_id: UserId) -> Result { + self.create_channel(name, None, creator_id).await } - pub async fn create_channel(&self, name: &str, parent: Option) -> Result { + pub async fn create_channel( + &self, + name: &str, + parent: Option, + creator_id: UserId, + ) -> Result { self.transaction(move |tx| async move { let tx = tx; @@ -3056,19 +3061,50 @@ impl Database { .await?; } + channel_member::ActiveModel { + channel_id: ActiveValue::Set(channel.id), + user_id: ActiveValue::Set(creator_id), + accepted: ActiveValue::Set(true), + admin: ActiveValue::Set(true), + ..Default::default() + } + .insert(&*tx) + .await?; + Ok(channel.id) }) .await } - // Property: Members are only - pub async fn add_channel_member(&self, channel_id: ChannelId, user_id: UserId) -> Result<()> { + pub async fn invite_channel_member( + &self, + channel_id: ChannelId, + invitee_id: UserId, + inviter_id: UserId, + is_admin: bool, + ) -> Result<()> { self.transaction(move |tx| async move { let tx = tx; + // Check if inviter is a member + channel_member::Entity::find() + .filter( + channel_member::Column::ChannelId + .eq(channel_id) + .and(channel_member::Column::UserId.eq(inviter_id)) + .and(channel_member::Column::Admin.eq(true)), + ) + .one(&*tx) + .await? + .ok_or_else(|| { + anyhow!("Inviter does not have permissions to invite the invitee") + })?; + let channel_membership = channel_member::ActiveModel { channel_id: ActiveValue::Set(channel_id), - user_id: ActiveValue::Set(user_id), + user_id: ActiveValue::Set(invitee_id), + accepted: ActiveValue::Set(false), + admin: ActiveValue::Set(is_admin), ..Default::default() }; @@ -3079,6 +3115,50 @@ impl Database { .await } + pub async fn respond_to_channel_invite( + &self, + channel_id: ChannelId, + user_id: UserId, + accept: bool, + ) -> Result<()> { + self.transaction(move |tx| async move { + let tx = tx; + + let rows_affected = if accept { + channel_member::Entity::update_many() + .set(channel_member::ActiveModel { + accepted: ActiveValue::Set(accept), + ..Default::default() + }) + .filter( + channel_member::Column::ChannelId + .eq(channel_id) + .and(channel_member::Column::UserId.eq(user_id)) + .and(channel_member::Column::Accepted.eq(false)), + ) + .exec(&*tx) + .await? + .rows_affected + } else { + channel_member::ActiveModel { + channel_id: ActiveValue::Unchanged(channel_id), + user_id: ActiveValue::Unchanged(user_id), + ..Default::default() + } + .delete(&*tx) + .await? + .rows_affected + }; + + if rows_affected == 0 { + Err(anyhow!("no such invitation"))?; + } + + Ok(()) + }) + .await + } + pub async fn get_channels(&self, user_id: UserId) -> Result> { self.transaction(|tx| async move { let tx = tx; @@ -3087,7 +3167,7 @@ impl Database { WITH RECURSIVE channel_tree(child_id, parent_id, depth) AS ( SELECT channel_id as child_id, CAST(NULL as INTEGER) as parent_id, 0 FROM channel_members - WHERE user_id = $1 + WHERE user_id = $1 AND accepted UNION SELECT channel_parents.child_id, channel_parents.parent_id, channel_tree.depth + 1 FROM channel_parents, channel_tree @@ -3114,6 +3194,22 @@ impl Database { .await } + pub async fn get_channel(&self, channel_id: ChannelId) -> Result { + self.transaction(|tx| async move { + let tx = tx; + let channel = channel::Entity::find_by_id(channel_id) + .one(&*tx) + .await? + .ok_or_else(|| anyhow!("no such channel"))?; + Ok(Channel { + id: channel.id, + name: channel.name, + parent_id: None, + }) + }) + .await + } + async fn transaction(&self, f: F) -> Result where F: Send + Fn(TransactionHandle) -> Fut, diff --git a/crates/collab/src/db/channel_member.rs b/crates/collab/src/db/channel_member.rs index cad7f3853d..f0f1a852cb 100644 --- a/crates/collab/src/db/channel_member.rs +++ b/crates/collab/src/db/channel_member.rs @@ -10,6 +10,8 @@ pub struct Model { pub id: ChannelMemberId, pub channel_id: ChannelId, pub user_id: UserId, + pub accepted: bool, + pub admin: bool, } impl ActiveModelBehavior for ActiveModel {} diff --git a/crates/collab/src/db/tests.rs b/crates/collab/src/db/tests.rs index 53c35ef31b..03e9eb577b 100644 --- a/crates/collab/src/db/tests.rs +++ b/crates/collab/src/db/tests.rs @@ -894,18 +894,21 @@ test_both_dbs!(test_channels_postgres, test_channels_sqlite, db, { .unwrap() .user_id; - let zed_id = db.create_root_channel("zed").await.unwrap(); - let crdb_id = db.create_channel("crdb", Some(zed_id)).await.unwrap(); + let zed_id = db.create_root_channel("zed", a_id).await.unwrap(); + let crdb_id = db.create_channel("crdb", Some(zed_id), a_id).await.unwrap(); let livestreaming_id = db - .create_channel("livestreaming", Some(zed_id)) + .create_channel("livestreaming", Some(zed_id), a_id) + .await + .unwrap(); + let replace_id = db + .create_channel("replace", Some(zed_id), a_id) + .await + .unwrap(); + let rust_id = db.create_root_channel("rust", a_id).await.unwrap(); + let cargo_id = db + .create_channel("cargo", Some(rust_id), a_id) .await .unwrap(); - let replace_id = db.create_channel("replace", Some(zed_id)).await.unwrap(); - let rust_id = db.create_root_channel("rust").await.unwrap(); - let cargo_id = db.create_channel("cargo", Some(rust_id)).await.unwrap(); - - db.add_channel_member(zed_id, a_id).await.unwrap(); - db.add_channel_member(rust_id, a_id).await.unwrap(); let channels = db.get_channels(a_id).await.unwrap(); diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 14d785307d..3d95d484ee 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -2,7 +2,7 @@ mod connection_pool; use crate::{ auth, - db::{self, Database, ProjectId, RoomId, ServerId, User, UserId}, + db::{self, ChannelId, Database, ProjectId, RoomId, ServerId, User, UserId}, executor::Executor, AppState, Result, }; @@ -239,6 +239,10 @@ impl Server { .add_request_handler(request_contact) .add_request_handler(remove_contact) .add_request_handler(respond_to_contact_request) + .add_request_handler(create_channel) + .add_request_handler(invite_channel_member) + .add_request_handler(remove_channel_member) + .add_request_handler(respond_to_channel_invite) .add_request_handler(follow) .add_message_handler(unfollow) .add_message_handler(update_followers) @@ -2084,6 +2088,100 @@ async fn remove_contact( Ok(()) } +async fn create_channel( + request: proto::CreateChannel, + response: Response, + session: Session, +) -> Result<()> { + let db = session.db().await; + let id = db + .create_channel( + &request.name, + request.parent_id.map(|id| ChannelId::from_proto(id)), + session.user_id, + ) + .await?; + + let mut update = proto::UpdateChannels::default(); + update.channels.push(proto::Channel { + id: id.to_proto(), + name: request.name, + parent_id: request.parent_id, + }); + session.peer.send(session.connection_id, update)?; + response.send(proto::CreateChannelResponse { + channel_id: id.to_proto(), + })?; + + Ok(()) +} + +async fn invite_channel_member( + request: proto::InviteChannelMember, + response: Response, + session: Session, +) -> Result<()> { + let db = session.db().await; + let channel_id = ChannelId::from_proto(request.channel_id); + let channel = db.get_channel(channel_id).await?; + let invitee_id = UserId::from_proto(request.user_id); + db.invite_channel_member(channel_id, invitee_id, session.user_id, false) + .await?; + + let mut update = proto::UpdateChannels::default(); + update.channel_invitations.push(proto::Channel { + id: channel.id.to_proto(), + name: channel.name, + parent_id: None, + }); + for connection_id in session + .connection_pool() + .await + .user_connection_ids(invitee_id) + { + session.peer.send(connection_id, update.clone())?; + } + + response.send(proto::Ack {})?; + Ok(()) +} + +async fn remove_channel_member( + request: proto::RemoveChannelMember, + response: Response, + session: Session, +) -> Result<()> { + Ok(()) +} + +async fn respond_to_channel_invite( + request: proto::RespondToChannelInvite, + response: Response, + session: Session, +) -> Result<()> { + let db = session.db().await; + let channel_id = ChannelId::from_proto(request.channel_id); + let channel = db.get_channel(channel_id).await?; + db.respond_to_channel_invite(channel_id, session.user_id, request.accept) + .await?; + + let mut update = proto::UpdateChannels::default(); + update + .remove_channel_invitations + .push(channel_id.to_proto()); + if request.accept { + update.channels.push(proto::Channel { + id: channel.id.to_proto(), + name: channel.name, + parent_id: None, + }); + } + session.peer.send(session.connection_id, update)?; + response.send(proto::Ack {})?; + + Ok(()) +} + async fn update_diff_base(request: proto::UpdateDiffBase, session: Session) -> Result<()> { let project_id = ProjectId::from_proto(request.project_id); let project_connection_ids = session diff --git a/crates/collab/src/tests.rs b/crates/collab/src/tests.rs index 2e98cd9b4d..cf302d3b4d 100644 --- a/crates/collab/src/tests.rs +++ b/crates/collab/src/tests.rs @@ -7,7 +7,8 @@ use crate::{ use anyhow::anyhow; use call::ActiveCall; use client::{ - self, proto::PeerId, Client, Connection, Credentials, EstablishConnectionError, UserStore, + self, proto::PeerId, ChannelStore, Client, Connection, Credentials, EstablishConnectionError, + UserStore, }; use collections::{HashMap, HashSet}; use fs::FakeFs; @@ -33,9 +34,9 @@ use std::{ use util::http::FakeHttpClient; use workspace::Workspace; +mod channel_tests; mod integration_tests; mod randomized_integration_tests; -mod channel_tests; struct TestServer { app_state: Arc, @@ -187,6 +188,8 @@ impl TestServer { let fs = FakeFs::new(cx.background()); let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http, cx)); + let channel_store = + cx.add_model(|cx| ChannelStore::new(client.clone(), user_store.clone(), cx)); let app_state = Arc::new(workspace::AppState { client: client.clone(), user_store: user_store.clone(), @@ -218,6 +221,7 @@ impl TestServer { username: name.to_string(), state: Default::default(), user_store, + channel_store, fs, language_registry: Arc::new(LanguageRegistry::test()), }; @@ -320,6 +324,7 @@ struct TestClient { username: String, state: RefCell, pub user_store: ModelHandle, + pub channel_store: ModelHandle, language_registry: Arc, fs: Arc, } diff --git a/crates/collab/src/tests/channel_tests.rs b/crates/collab/src/tests/channel_tests.rs index 8ab33adcbf..4cc0d24d9b 100644 --- a/crates/collab/src/tests/channel_tests.rs +++ b/crates/collab/src/tests/channel_tests.rs @@ -1,85 +1,108 @@ +use client::Channel; use gpui::{executor::Deterministic, TestAppContext}; use std::sync::Arc; -use crate::db::Channel; - use super::TestServer; #[gpui::test] -async fn test_basic_channels(deterministic: Arc, cx: &mut TestAppContext) { +async fn test_basic_channels( + deterministic: Arc, + cx_a: &mut TestAppContext, + cx_b: &mut TestAppContext, +) { deterministic.forbid_parking(); let mut server = TestServer::start(&deterministic).await; - let client_a = server.create_client(cx, "user_a").await; - let a_id = crate::db::UserId(client_a.user_id().unwrap() as i32); - let db = server._test_db.db(); + let client_a = server.create_client(cx_a, "user_a").await; + let client_b = server.create_client(cx_b, "user_b").await; - let zed_id = db.create_root_channel("zed").await.unwrap(); - let crdb_id = db.create_channel("crdb", Some(zed_id)).await.unwrap(); - let livestreaming_id = db - .create_channel("livestreaming", Some(zed_id)) + let channel_a_id = client_a + .channel_store + .update(cx_a, |channel_store, _| { + channel_store.create_channel("channel-a", None) + }) .await .unwrap(); - let replace_id = db.create_channel("replace", Some(zed_id)).await.unwrap(); - let rust_id = db.create_root_channel("rust").await.unwrap(); - let cargo_id = db.create_channel("cargo", Some(rust_id)).await.unwrap(); - db.add_channel_member(zed_id, a_id).await.unwrap(); - db.add_channel_member(rust_id, a_id).await.unwrap(); - - let channels = db.get_channels(a_id).await.unwrap(); - assert_eq!( - channels, - vec![ - Channel { - id: zed_id, - name: "zed".to_string(), + client_a.channel_store.read_with(cx_a, |channels, _| { + assert_eq!( + channels.channels(), + &[Channel { + id: channel_a_id, + name: "channel-a".to_string(), parent_id: None, - }, - Channel { - id: rust_id, - name: "rust".to_string(), - parent_id: None, - }, - Channel { - id: crdb_id, - name: "crdb".to_string(), - parent_id: Some(zed_id), - }, - Channel { - id: livestreaming_id, - name: "livestreaming".to_string(), - parent_id: Some(zed_id), - }, - Channel { - id: replace_id, - name: "replace".to_string(), - parent_id: Some(zed_id), - }, - Channel { - id: cargo_id, - name: "cargo".to_string(), - parent_id: Some(rust_id), - } - ] - ); -} + }] + ) + }); -#[gpui::test] -async fn test_block_cycle_creation(deterministic: Arc, cx: &mut TestAppContext) { - deterministic.forbid_parking(); - let mut server = TestServer::start(&deterministic).await; - let client_a = server.create_client(cx, "user_a").await; - let a_id = crate::db::UserId(client_a.user_id().unwrap() as i32); - let db = server._test_db.db(); + client_b + .channel_store + .read_with(cx_b, |channels, _| assert_eq!(channels.channels(), &[])); - let zed_id = db.create_root_channel("zed").await.unwrap(); - let first_id = db.create_channel("first", Some(zed_id)).await.unwrap(); - let second_id = db - .create_channel("second_id", Some(first_id)) + // Invite client B to channel A as client A. + client_a + .channel_store + .update(cx_a, |channel_store, _| { + channel_store.invite_member(channel_a_id, client_b.user_id().unwrap(), false) + }) .await .unwrap(); + + // Wait for client b to see the invitation + deterministic.run_until_parked(); + + client_b.channel_store.read_with(cx_b, |channels, _| { + assert_eq!( + channels.channel_invitations(), + &[Channel { + id: channel_a_id, + name: "channel-a".to_string(), + parent_id: None, + }] + ) + }); + + // Client B now sees that they are in channel A. + client_b + .channel_store + .update(cx_b, |channels, _| { + channels.respond_to_channel_invite(channel_a_id, true) + }) + .await + .unwrap(); + client_b.channel_store.read_with(cx_b, |channels, _| { + assert_eq!(channels.channel_invitations(), &[]); + assert_eq!( + channels.channels(), + &[Channel { + id: channel_a_id, + name: "channel-a".to_string(), + parent_id: None, + }] + ) + }); } +// TODO: +// Invariants to test: +// 1. Dag structure is maintained for all operations (can't make a cycle) +// 2. Can't be a member of a super channel, and accept a membership of a sub channel (by definition, a noop) + +// #[gpui::test] +// async fn test_block_cycle_creation(deterministic: Arc, cx: &mut TestAppContext) { +// // deterministic.forbid_parking(); +// // let mut server = TestServer::start(&deterministic).await; +// // let client_a = server.create_client(cx, "user_a").await; +// // let a_id = crate::db::UserId(client_a.user_id().unwrap() as i32); +// // let db = server._test_db.db(); + +// // let zed_id = db.create_root_channel("zed", a_id).await.unwrap(); +// // let first_id = db.create_channel("first", Some(zed_id)).await.unwrap(); +// // let second_id = db +// // .create_channel("second_id", Some(first_id)) +// // .await +// // .unwrap(); +// } + /* Linear things: - A way of expressing progress to the team diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index a0b98372b1..38ffbe6b7e 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -102,17 +102,6 @@ message Envelope { SearchProject search_project = 80; SearchProjectResponse search_project_response = 81; - GetChannels get_channels = 82; - GetChannelsResponse get_channels_response = 83; - JoinChannel join_channel = 84; - JoinChannelResponse join_channel_response = 85; - LeaveChannel leave_channel = 86; - SendChannelMessage send_channel_message = 87; - SendChannelMessageResponse send_channel_message_response = 88; - ChannelMessageSent channel_message_sent = 89; - GetChannelMessages get_channel_messages = 90; - GetChannelMessagesResponse get_channel_messages_response = 91; - UpdateContacts update_contacts = 92; UpdateInviteInfo update_invite_info = 93; ShowContacts show_contacts = 94; @@ -140,6 +129,13 @@ message Envelope { InlayHints inlay_hints = 116; InlayHintsResponse inlay_hints_response = 117; RefreshInlayHints refresh_inlay_hints = 118; + + CreateChannel create_channel = 119; + CreateChannelResponse create_channel_response = 120; + InviteChannelMember invite_channel_member = 121; + RemoveChannelMember remove_channel_member = 122; + RespondToChannelInvite respond_to_channel_invite = 123; + UpdateChannels update_channels = 124; } } @@ -867,23 +863,36 @@ message LspDiskBasedDiagnosticsUpdating {} message LspDiskBasedDiagnosticsUpdated {} -message GetChannels {} - -message GetChannelsResponse { +message UpdateChannels { repeated Channel channels = 1; + repeated uint64 remove_channels = 2; + repeated Channel channel_invitations = 3; + repeated uint64 remove_channel_invitations = 4; } -message JoinChannel { +message CreateChannel { + string name = 1; + optional uint64 parent_id = 2; +} + +message CreateChannelResponse { uint64 channel_id = 1; } -message JoinChannelResponse { - repeated ChannelMessage messages = 1; - bool done = 2; +message InviteChannelMember { + uint64 channel_id = 1; + uint64 user_id = 2; + bool admin = 3; } -message LeaveChannel { +message RemoveChannelMember { uint64 channel_id = 1; + uint64 user_id = 2; +} + +message RespondToChannelInvite { + uint64 channel_id = 1; + bool accept = 2; } message GetUsers { @@ -918,31 +927,6 @@ enum ContactRequestResponse { Dismiss = 3; } -message SendChannelMessage { - uint64 channel_id = 1; - string body = 2; - Nonce nonce = 3; -} - -message SendChannelMessageResponse { - ChannelMessage message = 1; -} - -message ChannelMessageSent { - uint64 channel_id = 1; - ChannelMessage message = 2; -} - -message GetChannelMessages { - uint64 channel_id = 1; - uint64 before_message_id = 2; -} - -message GetChannelMessagesResponse { - repeated ChannelMessage messages = 1; - bool done = 2; -} - message UpdateContacts { repeated Contact contacts = 1; repeated uint64 remove_contacts = 2; @@ -1274,14 +1258,7 @@ message Nonce { message Channel { uint64 id = 1; string name = 2; -} - -message ChannelMessage { - uint64 id = 1; - string body = 2; - uint64 timestamp = 3; - uint64 sender_id = 4; - Nonce nonce = 5; + optional uint64 parent_id = 3; } message Contact { diff --git a/crates/rpc/src/proto.rs b/crates/rpc/src/proto.rs index e24d6cb4b7..1e9e93a2d0 100644 --- a/crates/rpc/src/proto.rs +++ b/crates/rpc/src/proto.rs @@ -143,9 +143,10 @@ messages!( (Call, Foreground), (CallCanceled, Foreground), (CancelCall, Foreground), - (ChannelMessageSent, Foreground), (CopyProjectEntry, Foreground), (CreateBufferForPeer, Foreground), + (CreateChannel, Foreground), + (CreateChannelResponse, Foreground), (CreateProjectEntry, Foreground), (CreateRoom, Foreground), (CreateRoomResponse, Foreground), @@ -158,10 +159,6 @@ messages!( (FormatBuffers, Foreground), (FormatBuffersResponse, Foreground), (FuzzySearchUsers, Foreground), - (GetChannelMessages, Foreground), - (GetChannelMessagesResponse, Foreground), - (GetChannels, Foreground), - (GetChannelsResponse, Foreground), (GetCodeActions, Background), (GetCodeActionsResponse, Background), (GetHover, Background), @@ -181,14 +178,12 @@ messages!( (GetUsers, Foreground), (Hello, Foreground), (IncomingCall, Foreground), + (InviteChannelMember, Foreground), (UsersResponse, Foreground), - (JoinChannel, Foreground), - (JoinChannelResponse, Foreground), (JoinProject, Foreground), (JoinProjectResponse, Foreground), (JoinRoom, Foreground), (JoinRoomResponse, Foreground), - (LeaveChannel, Foreground), (LeaveProject, Foreground), (LeaveRoom, Foreground), (OpenBufferById, Background), @@ -211,18 +206,18 @@ messages!( (RejoinRoom, Foreground), (RejoinRoomResponse, Foreground), (RemoveContact, Foreground), + (RemoveChannelMember, Foreground), (ReloadBuffers, Foreground), (ReloadBuffersResponse, Foreground), (RemoveProjectCollaborator, Foreground), (RenameProjectEntry, Foreground), (RequestContact, Foreground), (RespondToContactRequest, Foreground), + (RespondToChannelInvite, Foreground), (RoomUpdated, Foreground), (SaveBuffer, Foreground), (SearchProject, Background), (SearchProjectResponse, Background), - (SendChannelMessage, Foreground), - (SendChannelMessageResponse, Foreground), (ShareProject, Foreground), (ShareProjectResponse, Foreground), (ShowContacts, Foreground), @@ -235,6 +230,7 @@ messages!( (UpdateBuffer, Foreground), (UpdateBufferFile, Foreground), (UpdateContacts, Foreground), + (UpdateChannels, Foreground), (UpdateDiagnosticSummary, Foreground), (UpdateFollowers, Foreground), (UpdateInviteInfo, Foreground), @@ -260,13 +256,12 @@ request_messages!( (CopyProjectEntry, ProjectEntryResponse), (CreateProjectEntry, ProjectEntryResponse), (CreateRoom, CreateRoomResponse), + (CreateChannel, CreateChannelResponse), (DeclineCall, Ack), (DeleteProjectEntry, ProjectEntryResponse), (ExpandProjectEntry, ExpandProjectEntryResponse), (Follow, FollowResponse), (FormatBuffers, FormatBuffersResponse), - (GetChannelMessages, GetChannelMessagesResponse), - (GetChannels, GetChannelsResponse), (GetCodeActions, GetCodeActionsResponse), (GetHover, GetHoverResponse), (GetCompletions, GetCompletionsResponse), @@ -278,7 +273,7 @@ request_messages!( (GetProjectSymbols, GetProjectSymbolsResponse), (FuzzySearchUsers, UsersResponse), (GetUsers, UsersResponse), - (JoinChannel, JoinChannelResponse), + (InviteChannelMember, Ack), (JoinProject, JoinProjectResponse), (JoinRoom, JoinRoomResponse), (LeaveRoom, Ack), @@ -295,12 +290,13 @@ request_messages!( (RefreshInlayHints, Ack), (ReloadBuffers, ReloadBuffersResponse), (RequestContact, Ack), + (RemoveChannelMember, Ack), (RemoveContact, Ack), (RespondToContactRequest, Ack), + (RespondToChannelInvite, Ack), (RenameProjectEntry, ProjectEntryResponse), (SaveBuffer, BufferSaved), (SearchProject, SearchProjectResponse), - (SendChannelMessage, SendChannelMessageResponse), (ShareProject, ShareProjectResponse), (SynchronizeBuffers, SynchronizeBuffersResponse), (Test, Test), @@ -363,8 +359,6 @@ entity_messages!( UpdateDiffBase ); -entity_messages!(channel_id, ChannelMessageSent); - const KIB: usize = 1024; const MIB: usize = KIB * 1024; const MAX_BUFFER_LEN: usize = MIB;