Detailed changes
@@ -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>,
+ channel_invitations: Vec<Channel>,
+ client: Arc<Client>,
+ user_store: ModelHandle<UserStore>,
+ rpc_subscription: Subscription,
+}
+
+#[derive(Debug, PartialEq)]
+pub struct Channel {
+ pub id: u64,
+ pub name: String,
+ pub parent_id: Option<u64>,
+}
+
+impl Entity for ChannelStore {
+ type Event = ();
+}
+
+impl ChannelStore {
+ pub fn new(
+ client: Arc<Client>,
+ user_store: ModelHandle<UserStore>,
+ cx: &mut ModelContext<Self>,
+ ) -> 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<u64>,
+ ) -> impl Future<Output = Result<u64>> {
+ 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<Output = Result<()>> {
+ 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<Output = Result<()>> {
+ 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<Self>,
+ ) -> Task<Result<()>> {
+ todo!()
+ }
+
+ pub fn channel_members(
+ &self,
+ channel_id: u64,
+ cx: &mut ModelContext<Self>,
+ ) -> Task<Result<Vec<Arc<User>>>> {
+ todo!()
+ }
+
+ pub fn add_guest_channel(&self, channel_id: u64) -> Task<Result<()>> {
+ todo!()
+ }
+
+ async fn handle_update_channels(
+ this: ModelHandle<Self>,
+ message: TypedEnvelope<proto::UpdateChannels>,
+ _: Arc<Client>,
+ 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(())
+ }
+}
@@ -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::*;
@@ -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
);
@@ -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()
);
@@ -3032,11 +3032,16 @@ impl Database {
// channels
- pub async fn create_root_channel(&self, name: &str) -> Result<ChannelId> {
- self.create_channel(name, None).await
+ pub async fn create_root_channel(&self, name: &str, creator_id: UserId) -> Result<ChannelId> {
+ self.create_channel(name, None, creator_id).await
}
- pub async fn create_channel(&self, name: &str, parent: Option<ChannelId>) -> Result<ChannelId> {
+ pub async fn create_channel(
+ &self,
+ name: &str,
+ parent: Option<ChannelId>,
+ creator_id: UserId,
+ ) -> Result<ChannelId> {
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<Vec<Channel>> {
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<Channel> {
+ 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<F, Fut, T>(&self, f: F) -> Result<T>
where
F: Send + Fn(TransactionHandle) -> Fut,
@@ -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 {}
@@ -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();
@@ -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<proto::CreateChannel>,
+ 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<proto::InviteChannelMember>,
+ 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<proto::RemoveChannelMember>,
+ session: Session,
+) -> Result<()> {
+ Ok(())
+}
+
+async fn respond_to_channel_invite(
+ request: proto::RespondToChannelInvite,
+ response: Response<proto::RespondToChannelInvite>,
+ 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
@@ -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<AppState>,
@@ -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<TestClientState>,
pub user_store: ModelHandle<UserStore>,
+ pub channel_store: ModelHandle<ChannelStore>,
language_registry: Arc<LanguageRegistry>,
fs: Arc<FakeFs>,
}
@@ -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<Deterministic>, cx: &mut TestAppContext) {
+async fn test_basic_channels(
+ deterministic: Arc<Deterministic>,
+ 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(),
- parent_id: None,
- },
- Channel {
- id: rust_id,
- name: "rust".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: 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<Deterministic>, 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<Deterministic>, 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
@@ -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 {
@@ -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;