diff --git a/crates/channel/src/channel_store.rs b/crates/channel/src/channel_store.rs index e61e520b47..ec4267af86 100644 --- a/crates/channel/src/channel_store.rs +++ b/crates/channel/src/channel_store.rs @@ -323,6 +323,18 @@ impl ChannelStore { }) } + + pub fn move_channel(&mut self, channel_id: ChannelId, from_parent: Option, to: Option, cx: &mut ModelContext) -> Task> { + let client = self.client.clone(); + cx.spawn(|_, _| async move { + let _ = client + .request(proto::MoveChannel { channel_id, from_parent, to }) + .await?; + + Ok(()) + }) + } + pub fn invite_member( &mut self, channel_id: ChannelId, @@ -502,7 +514,7 @@ impl ChannelStore { pub fn remove_channel(&self, channel_id: ChannelId) -> impl Future> { let client = self.client.clone(); async move { - client.request(proto::RemoveChannel { channel_id }).await?; + client.request(proto::DeleteChannel { channel_id }).await?; Ok(()) } } @@ -690,17 +702,17 @@ impl ChannelStore { } } - let channels_changed = !payload.channels.is_empty() || !payload.remove_channels.is_empty(); + let channels_changed = !payload.channels.is_empty() || !payload.delete_channels.is_empty(); if channels_changed { - if !payload.remove_channels.is_empty() { + if !payload.delete_channels.is_empty() { self.channels_by_id - .retain(|channel_id, _| !payload.remove_channels.contains(channel_id)); + .retain(|channel_id, _| !payload.delete_channels.contains(channel_id)); self.channel_participants - .retain(|channel_id, _| !payload.remove_channels.contains(channel_id)); + .retain(|channel_id, _| !payload.delete_channels.contains(channel_id)); self.channels_with_admin_privileges - .retain(|channel_id| !payload.remove_channels.contains(channel_id)); + .retain(|channel_id| !payload.delete_channels.contains(channel_id)); - for channel_id in &payload.remove_channels { + for channel_id in &payload.delete_channels { let channel_id = *channel_id; if let Some(OpenedModelHandle::Open(buffer)) = self.opened_buffers.remove(&channel_id) diff --git a/crates/channel/src/channel_store_tests.rs b/crates/channel/src/channel_store_tests.rs index 22174f161b..1d3694866f 100644 --- a/crates/channel/src/channel_store_tests.rs +++ b/crates/channel/src/channel_store_tests.rs @@ -122,7 +122,7 @@ fn test_dangling_channel_paths(cx: &mut AppContext) { update_channels( &channel_store, proto::UpdateChannels { - remove_channels: vec![1, 2], + delete_channels: vec![1, 2], ..Default::default() }, cx, diff --git a/crates/collab/src/db/queries/channels.rs b/crates/collab/src/db/queries/channels.rs index 0eb6c45fe6..fa1e28546a 100644 --- a/crates/collab/src/db/queries/channels.rs +++ b/crates/collab/src/db/queries/channels.rs @@ -1,5 +1,7 @@ use super::*; +type ChannelDescendants = HashMap>; + impl Database { #[cfg(test)] pub async fn all_channels(&self) -> Result> { @@ -68,7 +70,6 @@ impl Database { ], ); tx.execute(channel_paths_stmt).await?; - } else { channel_path::Entity::insert(channel_path::ActiveModel { channel_id: ActiveValue::Set(channel.id), @@ -101,7 +102,7 @@ impl Database { .await } - pub async fn remove_channel( + pub async fn delete_channel( &self, channel_id: ChannelId, user_id: UserId, @@ -159,9 +160,7 @@ impl Database { let channel_paths_stmt = Statement::from_sql_and_values( self.pool.get_database_backend(), sql, - [ - channel_id.to_proto().into(), - ], + [channel_id.to_proto().into()], ); tx.execute(channel_paths_stmt).await?; @@ -335,6 +334,43 @@ impl Database { .await } + async fn get_all_channels( + &self, + parents_by_child_id: ChannelDescendants, + tx: &DatabaseTransaction, + ) -> Result> { + let mut channels = Vec::with_capacity(parents_by_child_id.len()); + { + let mut rows = channel::Entity::find() + .filter(channel::Column::Id.is_in(parents_by_child_id.keys().copied())) + .stream(&*tx) + .await?; + while let Some(row) = rows.next().await { + let row = row?; + + // As these rows are pulled from the map's keys, this unwrap is safe. + let parents = parents_by_child_id.get(&row.id).unwrap(); + if parents.len() > 0 { + for parent in parents { + channels.push(Channel { + id: row.id, + name: row.name.clone(), + parent_id: Some(*parent), + }); + } + } else { + channels.push(Channel { + id: row.id, + name: row.name, + parent_id: None, + }); + } + } + } + + Ok(channels) + } + pub async fn get_channels_for_user(&self, user_id: UserId) -> Result { self.transaction(|tx| async move { let tx = tx; @@ -352,40 +388,12 @@ impl Database { .get_channel_descendants(channel_memberships.iter().map(|m| m.channel_id), &*tx) .await?; - let channels_with_admin_privileges = channel_memberships .iter() .filter_map(|membership| membership.admin.then_some(membership.channel_id)) .collect(); - let mut channels = Vec::with_capacity(parents_by_child_id.len()); - { - let mut rows = channel::Entity::find() - .filter(channel::Column::Id.is_in(parents_by_child_id.keys().copied())) - .stream(&*tx) - .await?; - while let Some(row) = rows.next().await { - let row = row?; - - // As these rows are pulled from the map's keys, this unwrap is safe. - let parents = parents_by_child_id.get(&row.id).unwrap(); - if parents.len() > 0 { - for parent in parents { - channels.push(Channel { - id: row.id, - name: row.name.clone(), - parent_id: Some(*parent), - }); - } - } else { - channels.push(Channel { - id: row.id, - name: row.name, - parent_id: None, - }); - } - } - } + let channels = self.get_all_channels(parents_by_child_id, &tx).await?; #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] enum QueryUserIdsAndChannelIds { @@ -632,7 +640,7 @@ impl Database { &self, channel_ids: impl IntoIterator, tx: &DatabaseTransaction, - ) -> Result>> { + ) -> Result { let mut values = String::new(); for id in channel_ids { if !values.is_empty() { @@ -659,7 +667,7 @@ impl Database { let stmt = Statement::from_string(self.pool.get_database_backend(), sql); - let mut parents_by_child_id: HashMap> = HashMap::default(); + let mut parents_by_child_id: ChannelDescendants = HashMap::default(); let mut paths = channel_path::Entity::find() .from_raw_sql(stmt) .stream(tx) @@ -758,7 +766,7 @@ impl Database { from: ChannelId, to: ChannelId, tx: &DatabaseTransaction, - ) -> Result<()> { + ) -> Result { let to_ancestors = self.get_channel_ancestors(to, &*tx).await?; let from_descendants = self.get_channel_descendants([from], &*tx).await?; for ancestor in to_ancestors { @@ -767,8 +775,6 @@ impl Database { } } - - let sql = r#" INSERT INTO channel_paths (id_path, channel_id) @@ -806,8 +812,7 @@ impl Database { } } - - Ok(()) + Ok(from_descendants) } async fn remove_channel_from_parent( @@ -816,8 +821,6 @@ impl Database { parent: ChannelId, tx: &DatabaseTransaction, ) -> Result<()> { - - let sql = r#" DELETE FROM channel_paths WHERE @@ -826,14 +829,10 @@ impl Database { let channel_paths_stmt = Statement::from_sql_and_values( self.pool.get_database_backend(), sql, - [ - parent.to_proto().into(), - from.to_proto().into(), - ], + [parent.to_proto().into(), from.to_proto().into()], ); tx.execute(channel_paths_stmt).await?; - Ok(()) } @@ -846,19 +845,22 @@ impl Database { /// - (`None`, `Some(id)`) Link the channel without removing it from any of it's parents /// - (`Some(id)`, `None`) Remove a channel from a given parent, and leave other parents /// - (`Some(id)`, `Some(id)`) Move channel from one parent to another, leaving other parents + /// + /// Returns the channel that was moved + it's sub channels pub async fn move_channel( &self, user: UserId, from: ChannelId, from_parent: Option, to: Option, - ) -> Result<()> { + ) -> Result> { self.transaction(|tx| async move { // Note that even with these maxed permissions, this linking operation // is still insecure because you can't remove someone's permissions to a // channel if they've linked the channel to one where they're an admin. self.check_user_is_channel_admin(from, user, &*tx).await?; + let mut channel_descendants = None; if let Some(from_parent) = from_parent { self.check_user_is_channel_admin(from_parent, user, &*tx) .await?; @@ -870,10 +872,30 @@ impl Database { if let Some(to) = to { self.check_user_is_channel_admin(to, user, &*tx).await?; - self.link_channel(from, to, &*tx).await?; + channel_descendants = Some(self.link_channel(from, to, &*tx).await?); } - Ok(()) + let mut channel_descendants = match channel_descendants { + Some(channel_descendants) => channel_descendants, + None => self.get_channel_descendants([from], &*tx).await?, + }; + + // Repair the parent ID of the channel in case it was from a cached call + if let Some(channel) = channel_descendants.get_mut(&from) { + if let Some(from_parent) = from_parent { + channel.remove(&from_parent); + } + if let Some(to) = to { + channel.insert(to); + } + } + + + let channels = self + .get_all_channels(channel_descendants, &*tx) + .await?; + + Ok(channels) }) .await } diff --git a/crates/collab/src/db/tests/channel_tests.rs b/crates/collab/src/db/tests/channel_tests.rs index ec8f3b56e6..be0b0f20e6 100644 --- a/crates/collab/src/db/tests/channel_tests.rs +++ b/crates/collab/src/db/tests/channel_tests.rs @@ -181,11 +181,11 @@ async fn test_channels(db: &Arc) { ); // Remove a single channel - db.remove_channel(crdb_id, a_id).await.unwrap(); + db.delete_channel(crdb_id, a_id).await.unwrap(); assert!(db.get_channel(crdb_id, a_id).await.unwrap().is_none()); // Remove a channel tree - let (mut channel_ids, user_ids) = db.remove_channel(rust_id, a_id).await.unwrap(); + let (mut channel_ids, user_ids) = db.delete_channel(rust_id, a_id).await.unwrap(); channel_ids.sort(); assert_eq!(channel_ids, &[rust_id, cargo_id, cargo_ra_id]); assert_eq!(user_ids, &[a_id]); @@ -647,7 +647,8 @@ async fn test_channels_moving(db: &Arc) { ); // Make a link - db.move_channel(a_id, livestreaming_dag_sub_id, None, Some(livestreaming_id)) + let channels = db + .move_channel(a_id, livestreaming_dag_sub_id, None, Some(livestreaming_id)) .await .unwrap(); @@ -655,6 +656,24 @@ async fn test_channels_moving(db: &Arc) { // /- gpui2 /---------------------\ // zed - crdb - livestreaming - livestreaming_dag - livestreaming_dag_sub_id // \--------/ + + // make sure we're getting the new link + pretty_assertions::assert_eq!( + channels, + vec![ + Channel { + id: livestreaming_dag_sub_id, + name: "livestreaming_dag_sub".to_string(), + parent_id: Some(livestreaming_id), + }, + Channel { + id: livestreaming_dag_sub_id, + name: "livestreaming_dag_sub".to_string(), + parent_id: Some(livestreaming_dag_id), + }, + ] + ); + let result = db.get_channels_for_user(a_id).await.unwrap(); pretty_assertions::assert_eq!( result.channels, @@ -703,7 +722,7 @@ async fn test_channels_moving(db: &Arc) { ); // Make another link - db.move_channel(a_id, livestreaming_id, None, Some(gpui2_id)) + let channels = db.move_channel(a_id, livestreaming_id, None, Some(gpui2_id)) .await .unwrap(); @@ -711,6 +730,40 @@ async fn test_channels_moving(db: &Arc) { // /- gpui2 -\ /---------------------\ // zed - crdb -- livestreaming - livestreaming_dag - livestreaming_dag_sub_id // \---------/ + + // Make sure that we're correctly getting the full sub-dag + pretty_assertions::assert_eq!(channels, + vec![Channel { + id: livestreaming_id, + name: "livestreaming".to_string(), + parent_id: Some(gpui2_id), + }, + Channel { + id: livestreaming_id, + name: "livestreaming".to_string(), + parent_id: Some(zed_id), + }, + Channel { + id: livestreaming_id, + name: "livestreaming".to_string(), + parent_id: Some(crdb_id), + }, + Channel { + id: livestreaming_dag_id, + name: "livestreaming_dag".to_string(), + parent_id: Some(livestreaming_id), + }, + Channel { + id: livestreaming_dag_sub_id, + name: "livestreaming_dag_sub".to_string(), + parent_id: Some(livestreaming_id), + }, + Channel { + id: livestreaming_dag_sub_id, + name: "livestreaming_dag_sub".to_string(), + parent_id: Some(livestreaming_dag_id), + }]); + let result = db.get_channels_for_user(a_id).await.unwrap(); pretty_assertions::assert_eq!( result.channels, @@ -764,7 +817,7 @@ async fn test_channels_moving(db: &Arc) { ); // Remove that inner link - db.move_channel(a_id, livestreaming_dag_sub_id, Some(livestreaming_id), None) + let channels = db.move_channel(a_id, livestreaming_dag_sub_id, Some(livestreaming_id), None) .await .unwrap(); @@ -772,6 +825,20 @@ async fn test_channels_moving(db: &Arc) { // /- gpui2 -\ // zed - crdb -- livestreaming - livestreaming_dag - livestreaming_dag_sub // \---------/ + + // Make sure the recently removed link isn't returned + pretty_assertions::assert_eq!( + channels, + vec![ + Channel { + id: livestreaming_dag_sub_id, + name: "livestreaming_dag_sub".to_string(), + parent_id: Some(livestreaming_dag_id), + }, + ] + ); + + let result = db.get_channels_for_user(a_id).await.unwrap(); pretty_assertions::assert_eq!( result.channels, @@ -824,24 +891,10 @@ async fn test_channels_moving(db: &Arc) { .await .unwrap(); - // DAG is now: // /- gpui2 // zed - crdb -- livestreaming - livestreaming_dag - livestreaming_dag_sub // \---------/ - // - // zed/gpui2 - // zed/crdb - // zed/crdb/livestreaming - // - // zed/crdb/livestreaming - // zed/crdb/livestreaming/livestreaming_dag - // zed/crdb/livestreaming/livestreaming_dag/livestreaming_dag_sub - - // zed/livestreaming - // zed/livestreaming/livestreaming_dag - // zed/livestreaming/livestreaming_dag/livestreaming_dag_sub - // let result = db.get_channels_for_user(a_id).await.unwrap(); pretty_assertions::assert_eq!( result.channels, @@ -936,7 +989,7 @@ async fn test_channels_moving(db: &Arc) { ); // Deleting a channel should not delete children that still have other parents - db.remove_channel(gpui2_id, a_id).await.unwrap(); + db.delete_channel(gpui2_id, a_id).await.unwrap(); // DAG is now: // zed - crdb @@ -974,12 +1027,14 @@ async fn test_channels_moving(db: &Arc) { ); // But deleting a parent of a DAG should delete the whole DAG: - db.move_channel(a_id, livestreaming_id, None, Some(crdb_id)).await.unwrap(); + db.move_channel(a_id, livestreaming_id, None, Some(crdb_id)) + .await + .unwrap(); // DAG is now: // zed - crdb - livestreaming - livestreaming_dag - livestreaming_dag_sub // \--------/ - db.remove_channel(zed_id, a_id).await.unwrap(); + db.delete_channel(zed_id, a_id).await.unwrap(); let result = db.get_channels_for_user(a_id).await.unwrap(); assert!(result.channels.is_empty()) } diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 3289daf6ca..068a69fde1 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -250,7 +250,7 @@ impl Server { .add_request_handler(remove_contact) .add_request_handler(respond_to_contact_request) .add_request_handler(create_channel) - .add_request_handler(remove_channel) + .add_request_handler(delete_channel) .add_request_handler(invite_channel_member) .add_request_handler(remove_channel_member) .add_request_handler(set_channel_member_admin) @@ -267,6 +267,7 @@ impl Server { .add_request_handler(send_channel_message) .add_request_handler(remove_channel_message) .add_request_handler(get_channel_messages) + .add_request_handler(move_channel) .add_request_handler(follow) .add_message_handler(unfollow) .add_message_handler(update_followers) @@ -2230,23 +2231,23 @@ async fn create_channel( Ok(()) } -async fn remove_channel( - request: proto::RemoveChannel, - response: Response, +async fn delete_channel( + request: proto::DeleteChannel, + response: Response, session: Session, ) -> Result<()> { let db = session.db().await; let channel_id = request.channel_id; let (removed_channels, member_ids) = db - .remove_channel(ChannelId::from_proto(channel_id), session.user_id) + .delete_channel(ChannelId::from_proto(channel_id), session.user_id) .await?; response.send(proto::Ack {})?; // Notify members of removed channels let mut update = proto::UpdateChannels::default(); update - .remove_channels + .delete_channels .extend(removed_channels.into_iter().map(|id| id.to_proto())); let connection_pool = session.connection_pool().await; @@ -2306,7 +2307,7 @@ async fn remove_channel_member( .await?; let mut update = proto::UpdateChannels::default(); - update.remove_channels.push(channel_id.to_proto()); + update.delete_channels.push(channel_id.to_proto()); for connection_id in session .connection_pool() @@ -2390,6 +2391,66 @@ async fn rename_channel( Ok(()) } +async fn move_channel( + request: proto::MoveChannel, + response: Response, + session: Session, +) -> Result<()> { + let db = session.db().await; + let channel_id = ChannelId::from_proto(request.channel_id); + let from_parent = request.from_parent.map(ChannelId::from_proto); + let to = request.to.map(ChannelId::from_proto); + let channels = db + .move_channel( + session.user_id, + channel_id, + from_parent, + to, + ) + .await?; + + + if let Some(from_parent) = from_parent { + let members = db.get_channel_members(from_parent).await?; + let update = proto::UpdateChannels { + delete_channel_edge: vec![proto::ChannelEdge { + channel_id: channel_id.to_proto(), + parent_id: from_parent.to_proto(), + }], + ..Default::default() + }; + let connection_pool = session.connection_pool().await; + for member_id in members { + for connection_id in connection_pool.user_connection_ids(member_id) { + session.peer.send(connection_id, update.clone())?; + } + } + + } + + if let Some(to) = to { + let members = db.get_channel_members(to).await?; + let connection_pool = session.connection_pool().await; + let update = proto::UpdateChannels { + channels: channels.into_iter().map(|channel| proto::Channel { + id: channel.id.to_proto(), + name: channel.name, + parent_id: channel.parent_id.map(ChannelId::to_proto), + }).collect(), + ..Default::default() + }; + for member_id in members { + for connection_id in connection_pool.user_connection_ids(member_id) { + session.peer.send(connection_id, update.clone())?; + } + } + } + + response.send(Ack {})?; + + Ok(()) +} + async fn get_channel_members( request: proto::GetChannelMembers, response: Response, diff --git a/crates/collab/src/tests/channel_tests.rs b/crates/collab/src/tests/channel_tests.rs index b54b4d349b..77045d9174 100644 --- a/crates/collab/src/tests/channel_tests.rs +++ b/crates/collab/src/tests/channel_tests.rs @@ -874,6 +874,143 @@ async fn test_lost_channel_creation( ); } +#[gpui::test] +async fn test_channel_moving(deterministic: Arc, cx_a: &mut TestAppContext) { + deterministic.forbid_parking(); + let mut server = TestServer::start(&deterministic).await; + let client_a = server.create_client(cx_a, "user_a").await; + + let channel_a_id = client_a + .channel_store() + .update(cx_a, |channel_store, cx| { + channel_store.create_channel("channel-a", None, cx) + }) + .await + .unwrap(); + let channel_b_id = client_a + .channel_store() + .update(cx_a, |channel_store, cx| { + channel_store.create_channel("channel-b", Some(channel_a_id), cx) + }) + .await + .unwrap(); + let channel_c_id = client_a + .channel_store() + .update(cx_a, |channel_store, cx| { + channel_store.create_channel("channel-c", Some(channel_b_id), cx) + }) + .await + .unwrap(); + + // Current shape: + // a - b - c + deterministic.run_until_parked(); + assert_channels( + client_a.channel_store(), + cx_a, + &[ + ExpectedChannel { + id: channel_a_id, + name: "channel-a".to_string(), + depth: 0, + user_is_admin: true, + }, + ExpectedChannel { + id: channel_b_id, + name: "channel-b".to_string(), + depth: 1, + user_is_admin: true, + }, + ExpectedChannel { + id: channel_c_id, + name: "channel-c".to_string(), + depth: 2, + user_is_admin: true, + }, + ], + ); + + client_a + .channel_store() + .update(cx_a, |channel_store, cx| { + channel_store.move_channel(channel_c_id, Some(channel_b_id), Some(channel_a_id), cx) + }) + .await + .unwrap(); + + // Current shape: + // /- c + // a -- b + deterministic.run_until_parked(); + assert_channels( + client_a.channel_store(), + cx_a, + &[ + ExpectedChannel { + id: channel_a_id, + name: "channel-a".to_string(), + depth: 0, + user_is_admin: true, + }, + ExpectedChannel { + id: channel_b_id, + name: "channel-b".to_string(), + depth: 1, + user_is_admin: true, + }, + ExpectedChannel { + id: channel_c_id, + name: "channel-c".to_string(), + depth: 1, + user_is_admin: true, + }, + ], + ); + + client_a + .channel_store() + .update(cx_a, |channel_store, cx| { + channel_store.move_channel(channel_c_id, None, Some(channel_b_id), cx) + }) + .await + .unwrap(); + + // Current shape: + // /------\ + // a -- b -- c + deterministic.run_until_parked(); + assert_channels( + client_a.channel_store(), + cx_a, + &[ + ExpectedChannel { + id: channel_a_id, + name: "channel-a".to_string(), + depth: 0, + user_is_admin: true, + }, + ExpectedChannel { + id: channel_b_id, + name: "channel-b".to_string(), + depth: 1, + user_is_admin: true, + }, + ExpectedChannel { + id: channel_c_id, + name: "channel-c".to_string(), + depth: 2, + user_is_admin: true, + }, + ExpectedChannel { + id: channel_c_id, + name: "channel-c".to_string(), + depth: 1, + user_is_admin: true, + }, + ], + ); +} + #[derive(Debug, PartialEq)] struct ExpectedChannel { depth: usize, @@ -920,5 +1057,5 @@ fn assert_channels( }) .collect::>() }); - assert_eq!(actual, expected_channels); + pretty_assertions::assert_eq!(actual, expected_channels); } diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index 855588a2d8..f252efaa14 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -141,7 +141,7 @@ message Envelope { RespondToChannelInvite respond_to_channel_invite = 123; UpdateChannels update_channels = 124; JoinChannel join_channel = 125; - RemoveChannel remove_channel = 126; + DeleteChannel delete_channel = 126; GetChannelMembers get_channel_members = 127; GetChannelMembersResponse get_channel_members_response = 128; SetChannelMemberAdmin set_channel_member_admin = 129; @@ -165,7 +165,9 @@ message Envelope { ChannelMessageSent channel_message_sent = 147; GetChannelMessages get_channel_messages = 148; GetChannelMessagesResponse get_channel_messages_response = 149; - RemoveChannelMessage remove_channel_message = 150; // Current max + RemoveChannelMessage remove_channel_message = 150; + + MoveChannel move_channel = 151; // Current max } } @@ -955,11 +957,17 @@ message LspDiskBasedDiagnosticsUpdated {} message UpdateChannels { repeated Channel channels = 1; - repeated uint64 remove_channels = 2; - repeated Channel channel_invitations = 3; - repeated uint64 remove_channel_invitations = 4; - repeated ChannelParticipants channel_participants = 5; - repeated ChannelPermission channel_permissions = 6; + repeated ChannelEdge delete_channel_edge = 2; + repeated uint64 delete_channels = 3; + repeated Channel channel_invitations = 4; + repeated uint64 remove_channel_invitations = 5; + repeated ChannelParticipants channel_participants = 6; + repeated ChannelPermission channel_permissions = 7; +} + +message ChannelEdge { + uint64 channel_id = 1; + uint64 parent_id = 2; } message ChannelPermission { @@ -976,7 +984,7 @@ message JoinChannel { uint64 channel_id = 1; } -message RemoveChannel { +message DeleteChannel { uint64 channel_id = 1; } @@ -1074,6 +1082,12 @@ message GetChannelMessagesResponse { bool done = 2; } +message MoveChannel { + uint64 channel_id = 1; + optional uint64 from_parent = 2; + optional uint64 to = 3; +} + message JoinChannelBuffer { uint64 channel_id = 1; } diff --git a/crates/rpc/src/proto.rs b/crates/rpc/src/proto.rs index 240daed1b2..121f49b966 100644 --- a/crates/rpc/src/proto.rs +++ b/crates/rpc/src/proto.rs @@ -246,7 +246,8 @@ messages!( (UpdateBuffer, Foreground), (UpdateBufferFile, Foreground), (UpdateContacts, Foreground), - (RemoveChannel, Foreground), + (DeleteChannel, Foreground), + (MoveChannel, Foreground), (UpdateChannels, Foreground), (UpdateDiagnosticSummary, Foreground), (UpdateFollowers, Foreground), @@ -329,8 +330,10 @@ request_messages!( (JoinChannel, JoinRoomResponse), (RemoveChannel, Ack), (RemoveChannelMessage, Ack), + (DeleteChannel, Ack), (RenameProjectEntry, ProjectEntryResponse), (RenameChannel, ChannelResponse), + (MoveChannel, Ack), (SaveBuffer, BufferSaved), (SearchProject, SearchProjectResponse), (ShareProject, ShareProjectResponse),