From 0ce1ec5d15158a5ff83c4221b9d59325f032bef5 Mon Sep 17 00:00:00 2001 From: Mikayla Date: Wed, 18 Oct 2023 05:28:05 -0700 Subject: [PATCH] Restrict DAG-related functionality, but retain infrastructure for implementing symlinks --- crates/channel/src/channel_store_tests.rs | 16 +- crates/collab/src/db/queries/channels.rs | 52 +++- crates/collab/src/rpc.rs | 125 ++++---- crates/collab/src/tests/channel_tests.rs | 345 +++++++++++----------- crates/collab_ui/src/collab_panel.rs | 188 +++--------- crates/rpc/proto/zed.proto | 6 + 6 files changed, 338 insertions(+), 394 deletions(-) diff --git a/crates/channel/src/channel_store_tests.rs b/crates/channel/src/channel_store_tests.rs index 6a9560f608..69c0cd37fc 100644 --- a/crates/channel/src/channel_store_tests.rs +++ b/crates/channel/src/channel_store_tests.rs @@ -19,17 +19,15 @@ fn test_update_channels(cx: &mut AppContext) { id: 1, name: "b".to_string(), visibility: proto::ChannelVisibility::Members as i32, + role: proto::ChannelRole::Admin.into(), }, proto::Channel { id: 2, name: "a".to_string(), visibility: proto::ChannelVisibility::Members as i32, + role: proto::ChannelRole::Member.into(), }, ], - channel_permissions: vec![proto::ChannelPermission { - channel_id: 1, - role: proto::ChannelRole::Admin.into(), - }], ..Default::default() }, cx, @@ -52,11 +50,13 @@ fn test_update_channels(cx: &mut AppContext) { id: 3, name: "x".to_string(), visibility: proto::ChannelVisibility::Members as i32, + role: proto::ChannelRole::Member.into(), }, proto::Channel { id: 4, name: "y".to_string(), visibility: proto::ChannelVisibility::Members as i32, + role: proto::ChannelRole::Member.into(), }, ], insert_edge: vec![ @@ -97,16 +97,19 @@ fn test_dangling_channel_paths(cx: &mut AppContext) { id: 0, name: "a".to_string(), visibility: proto::ChannelVisibility::Members as i32, + role: proto::ChannelRole::Admin.into(), }, proto::Channel { id: 1, name: "b".to_string(), visibility: proto::ChannelVisibility::Members as i32, + role: proto::ChannelRole::Admin.into(), }, proto::Channel { id: 2, name: "c".to_string(), visibility: proto::ChannelVisibility::Members as i32, + role: proto::ChannelRole::Admin.into(), }, ], insert_edge: vec![ @@ -119,10 +122,6 @@ fn test_dangling_channel_paths(cx: &mut AppContext) { channel_id: 2, }, ], - channel_permissions: vec![proto::ChannelPermission { - channel_id: 0, - role: proto::ChannelRole::Admin.into(), - }], ..Default::default() }, cx, @@ -166,6 +165,7 @@ async fn test_channel_messages(cx: &mut TestAppContext) { id: channel_id, name: "the-channel".to_string(), visibility: proto::ChannelVisibility::Members as i32, + role: proto::ChannelRole::Admin.into(), }], ..Default::default() }); diff --git a/crates/collab/src/db/queries/channels.rs b/crates/collab/src/db/queries/channels.rs index 65393e07f8..575f55fe02 100644 --- a/crates/collab/src/db/queries/channels.rs +++ b/crates/collab/src/db/queries/channels.rs @@ -419,7 +419,7 @@ impl Database { } let channels = channel::Entity::find() - .filter(channel::Column::Id.is_in(role_for_channel.keys().cloned())) + .filter(channel::Column::Id.is_in(role_for_channel.keys().copied())) .all(&*tx) .await?; @@ -633,6 +633,36 @@ impl Database { .await } + pub async fn get_channel_members_and_roles( + &self, + id: ChannelId, + ) -> Result> { + self.transaction(|tx| async move { + #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] + enum QueryUserIdsAndRoles { + UserId, + Role, + } + + let ancestor_ids = self.get_channel_ancestors(id, &*tx).await?; + let user_ids_and_roles = channel_member::Entity::find() + .distinct() + .filter( + channel_member::Column::ChannelId + .is_in(ancestor_ids.iter().copied()) + .and(channel_member::Column::Accepted.eq(true)), + ) + .select_only() + .column(channel_member::Column::UserId) + .column(channel_member::Column::Role) + .into_values::<_, QueryUserIdsAndRoles>() + .all(&*tx) + .await?; + Ok(user_ids_and_roles) + }) + .await + } + pub async fn set_channel_member_role( &self, channel_id: ChannelId, @@ -1138,9 +1168,6 @@ impl Database { to: ChannelId, ) -> 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(channel, user, &*tx) .await?; @@ -1327,6 +1354,23 @@ impl Database { }) .await } + + pub async fn assert_root_channel(&self, channel: ChannelId) -> Result<()> { + self.transaction(|tx| async move { + let path = channel_path::Entity::find() + .filter(channel_path::Column::ChannelId.eq(channel)) + .one(&*tx) + .await? + .ok_or_else(|| anyhow!("no such channel found"))?; + + let mut id_parts = path.id_path.trim_matches('/').split('/'); + + (id_parts.next().is_some() && id_parts.next().is_none()) + .then_some(()) + .ok_or_else(|| anyhow!("channel is not a root channel").into()) + }) + .await + } } #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index aa8a4552b3..c7a2ba88b1 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -3,8 +3,8 @@ mod connection_pool; use crate::{ auth, db::{ - self, BufferId, ChannelId, ChannelsForUser, Database, MessageId, ProjectId, RoomId, - ServerId, User, UserId, + self, BufferId, ChannelId, ChannelRole, ChannelVisibility, ChannelsForUser, Database, + MessageId, ProjectId, RoomId, ServerId, User, UserId, }, executor::Executor, AppState, Result, @@ -38,8 +38,8 @@ use lazy_static::lazy_static; use prometheus::{register_int_gauge, IntGauge}; use rpc::{ proto::{ - self, Ack, AnyTypedEnvelope, ChannelEdge, EntityMessage, EnvelopedMessage, - LiveKitConnectionInfo, RequestMessage, UpdateChannelBufferCollaborators, + self, Ack, AnyTypedEnvelope, EntityMessage, EnvelopedMessage, LiveKitConnectionInfo, + RequestMessage, UpdateChannelBufferCollaborators, }, Connection, ConnectionId, Peer, Receipt, TypedEnvelope, }; @@ -2366,15 +2366,18 @@ async fn set_channel_visibility( for member in members { for connection_id in connection_pool.user_connection_ids(UserId::from_proto(member.user_id)) { - let mut update = proto::UpdateChannels::default(); - update.channels.push(proto::Channel { - id: channel.id.to_proto(), - name: channel.name.clone(), - visibility: channel.visibility.into(), - role: member.role.into(), - }); - - session.peer.send(connection_id, update.clone())?; + session.peer.send( + connection_id, + proto::UpdateChannels { + channels: vec![proto::Channel { + id: channel.id.to_proto(), + name: channel.name.clone(), + visibility: channel.visibility.into(), + role: member.role.into(), + }], + ..Default::default() + }, + )?; } } @@ -2468,6 +2471,8 @@ async fn rename_channel( Ok(()) } +// TODO: Implement in terms of symlinks +// Current behavior of this is more like 'Move root channel' async fn link_channel( request: proto::LinkChannel, response: Response, @@ -2476,30 +2481,46 @@ async fn link_channel( let db = session.db().await; let channel_id = ChannelId::from_proto(request.channel_id); let to = ChannelId::from_proto(request.to); - let channels_to_send = db.link_channel(session.user_id, channel_id, to).await?; - let members = db.get_channel_members(to).await?; + // TODO: Remove this restriction once we have symlinks + db.assert_root_channel(channel_id).await?; + + let channels_to_send = db.link_channel(session.user_id, channel_id, to).await?; + let members = db.get_channel_members_and_roles(to).await?; let connection_pool = session.connection_pool().await; - let update = proto::UpdateChannels { - channels: channels_to_send - .channels - .into_iter() - .map(|channel| proto::Channel { - id: channel.id.to_proto(), - visibility: channel.visibility.into(), - name: channel.name, - // TODO: not all these members should be able to see all those channels - // the channels in channels_to_send are from the admin point of view, - // but any public guests should only get updates about public channels. - role: todo!(), - }) - .collect(), - insert_edge: channels_to_send.edges, - ..Default::default() - }; - for member_id in members { + + for (member_id, role) in members { + let build_channel_proto = |channel: &db::Channel| proto::Channel { + id: channel.id.to_proto(), + visibility: channel.visibility.into(), + name: channel.name.clone(), + role: role.into(), + }; + for connection_id in connection_pool.user_connection_ids(member_id) { - session.peer.send(connection_id, update.clone())?; + let channels: Vec<_> = if role == ChannelRole::Guest { + channels_to_send + .channels + .iter() + .filter(|channel| channel.visibility != ChannelVisibility::Public) + .map(build_channel_proto) + .collect() + } else { + channels_to_send + .channels + .iter() + .map(build_channel_proto) + .collect() + }; + + session.peer.send( + connection_id, + proto::UpdateChannels { + channels, + insert_edge: channels_to_send.edges.clone(), + ..Default::default() + }, + )?; } } @@ -2508,36 +2529,13 @@ async fn link_channel( Ok(()) } +// TODO: Implement in terms of symlinks async fn unlink_channel( - request: proto::UnlinkChannel, - response: Response, - session: Session, + _request: proto::UnlinkChannel, + _response: Response, + _session: Session, ) -> Result<()> { - let db = session.db().await; - let channel_id = ChannelId::from_proto(request.channel_id); - let from = ChannelId::from_proto(request.from); - - db.unlink_channel(session.user_id, channel_id, from).await?; - - let members = db.get_channel_members(from).await?; - - let update = proto::UpdateChannels { - delete_edge: vec![proto::ChannelEdge { - channel_id: channel_id.to_proto(), - parent_id: from.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())?; - } - } - - response.send(Ack {})?; - - Ok(()) + Err(anyhow!("unimplemented").into()) } async fn move_channel( @@ -2554,7 +2552,7 @@ async fn move_channel( .public_path_to_channel(from_parent) .await? .last() - .cloned(); + .copied(); let channels_to_send = db .move_channel(session.user_id, channel_id, from_parent, to) @@ -2574,6 +2572,7 @@ async fn move_channel( .filter(|member| { member.role() == proto::ChannelRole::Admin || member.role() == proto::ChannelRole::Guest }); + let members_to = db .get_channel_participant_details(to, session.user_id) .await? diff --git a/crates/collab/src/tests/channel_tests.rs b/crates/collab/src/tests/channel_tests.rs index af4b99c4ef..f982f05ee3 100644 --- a/crates/collab/src/tests/channel_tests.rs +++ b/crates/collab/src/tests/channel_tests.rs @@ -1031,14 +1031,14 @@ async fn test_invite_access( async fn test_channel_moving( deterministic: Arc, cx_a: &mut TestAppContext, - cx_b: &mut TestAppContext, - cx_c: &mut TestAppContext, + _cx_b: &mut TestAppContext, + _cx_c: &mut TestAppContext, ) { deterministic.forbid_parking(); let mut server = TestServer::start(&deterministic).await; let client_a = server.create_client(cx_a, "user_a").await; - let client_b = server.create_client(cx_b, "user_b").await; - let client_c = server.create_client(cx_c, "user_c").await; + // let client_b = server.create_client(cx_b, "user_b").await; + // let client_c = server.create_client(cx_c, "user_c").await; let channels = server .make_channel_tree( @@ -1091,187 +1091,188 @@ async fn test_channel_moving( ], ); - client_a - .channel_store() - .update(cx_a, |channel_store, cx| { - channel_store.link_channel(channel_d_id, channel_c_id, cx) - }) - .await - .unwrap(); + // TODO: Restore this test once we have a way to make channel symlinks + // client_a + // .channel_store() + // .update(cx_a, |channel_store, cx| { + // channel_store.link_channel(channel_d_id, channel_c_id, cx) + // }) + // .await + // .unwrap(); - // Current shape for A: - // /------\ - // a - b -- c -- d - assert_channels_list_shape( - client_a.channel_store(), - cx_a, - &[ - (channel_a_id, 0), - (channel_b_id, 1), - (channel_c_id, 2), - (channel_d_id, 3), - (channel_d_id, 2), - ], - ); + // // Current shape for A: + // // /------\ + // // a - b -- c -- d + // assert_channels_list_shape( + // client_a.channel_store(), + // cx_a, + // &[ + // (channel_a_id, 0), + // (channel_b_id, 1), + // (channel_c_id, 2), + // (channel_d_id, 3), + // (channel_d_id, 2), + // ], + // ); + // + // let b_channels = server + // .make_channel_tree( + // &[ + // ("channel-mu", None), + // ("channel-gamma", Some("channel-mu")), + // ("channel-epsilon", Some("channel-mu")), + // ], + // (&client_b, cx_b), + // ) + // .await; + // let channel_mu_id = b_channels[0]; + // let channel_ga_id = b_channels[1]; + // let channel_ep_id = b_channels[2]; - let b_channels = server - .make_channel_tree( - &[ - ("channel-mu", None), - ("channel-gamma", Some("channel-mu")), - ("channel-epsilon", Some("channel-mu")), - ], - (&client_b, cx_b), - ) - .await; - let channel_mu_id = b_channels[0]; - let channel_ga_id = b_channels[1]; - let channel_ep_id = b_channels[2]; + // // Current shape for B: + // // /- ep + // // mu -- ga + // assert_channels_list_shape( + // client_b.channel_store(), + // cx_b, + // &[(channel_mu_id, 0), (channel_ep_id, 1), (channel_ga_id, 1)], + // ); - // Current shape for B: - // /- ep - // mu -- ga - assert_channels_list_shape( - client_b.channel_store(), - cx_b, - &[(channel_mu_id, 0), (channel_ep_id, 1), (channel_ga_id, 1)], - ); + // client_a + // .add_admin_to_channel((&client_b, cx_b), channel_b_id, cx_a) + // .await; - client_a - .add_admin_to_channel((&client_b, cx_b), channel_b_id, cx_a) - .await; + // // Current shape for B: + // // /- ep + // // mu -- ga + // // /---------\ + // // b -- c -- d + // assert_channels_list_shape( + // client_b.channel_store(), + // cx_b, + // &[ + // // New channels from a + // (channel_b_id, 0), + // (channel_c_id, 1), + // (channel_d_id, 2), + // (channel_d_id, 1), + // // B's old channels + // (channel_mu_id, 0), + // (channel_ep_id, 1), + // (channel_ga_id, 1), + // ], + // ); - // Current shape for B: - // /- ep - // mu -- ga - // /---------\ - // b -- c -- d - assert_channels_list_shape( - client_b.channel_store(), - cx_b, - &[ - // New channels from a - (channel_b_id, 0), - (channel_c_id, 1), - (channel_d_id, 2), - (channel_d_id, 1), - // B's old channels - (channel_mu_id, 0), - (channel_ep_id, 1), - (channel_ga_id, 1), - ], - ); + // client_b + // .add_admin_to_channel((&client_c, cx_c), channel_ep_id, cx_b) + // .await; - client_b - .add_admin_to_channel((&client_c, cx_c), channel_ep_id, cx_b) - .await; + // // Current shape for C: + // // - ep + // assert_channels_list_shape(client_c.channel_store(), cx_c, &[(channel_ep_id, 0)]); - // Current shape for C: - // - ep - assert_channels_list_shape(client_c.channel_store(), cx_c, &[(channel_ep_id, 0)]); + // client_b + // .channel_store() + // .update(cx_b, |channel_store, cx| { + // channel_store.link_channel(channel_b_id, channel_ep_id, cx) + // }) + // .await + // .unwrap(); - client_b - .channel_store() - .update(cx_b, |channel_store, cx| { - channel_store.link_channel(channel_b_id, channel_ep_id, cx) - }) - .await - .unwrap(); + // // Current shape for B: + // // /---------\ + // // /- ep -- b -- c -- d + // // mu -- ga + // assert_channels_list_shape( + // client_b.channel_store(), + // cx_b, + // &[ + // (channel_mu_id, 0), + // (channel_ep_id, 1), + // (channel_b_id, 2), + // (channel_c_id, 3), + // (channel_d_id, 4), + // (channel_d_id, 3), + // (channel_ga_id, 1), + // ], + // ); - // Current shape for B: - // /---------\ - // /- ep -- b -- c -- d - // mu -- ga - assert_channels_list_shape( - client_b.channel_store(), - cx_b, - &[ - (channel_mu_id, 0), - (channel_ep_id, 1), - (channel_b_id, 2), - (channel_c_id, 3), - (channel_d_id, 4), - (channel_d_id, 3), - (channel_ga_id, 1), - ], - ); + // // Current shape for C: + // // /---------\ + // // ep -- b -- c -- d + // assert_channels_list_shape( + // client_c.channel_store(), + // cx_c, + // &[ + // (channel_ep_id, 0), + // (channel_b_id, 1), + // (channel_c_id, 2), + // (channel_d_id, 3), + // (channel_d_id, 2), + // ], + // ); - // Current shape for C: - // /---------\ - // ep -- b -- c -- d - assert_channels_list_shape( - client_c.channel_store(), - cx_c, - &[ - (channel_ep_id, 0), - (channel_b_id, 1), - (channel_c_id, 2), - (channel_d_id, 3), - (channel_d_id, 2), - ], - ); + // client_b + // .channel_store() + // .update(cx_b, |channel_store, cx| { + // channel_store.link_channel(channel_ga_id, channel_b_id, cx) + // }) + // .await + // .unwrap(); - client_b - .channel_store() - .update(cx_b, |channel_store, cx| { - channel_store.link_channel(channel_ga_id, channel_b_id, cx) - }) - .await - .unwrap(); + // // Current shape for B: + // // /---------\ + // // /- ep -- b -- c -- d + // // / \ + // // mu ---------- ga + // assert_channels_list_shape( + // client_b.channel_store(), + // cx_b, + // &[ + // (channel_mu_id, 0), + // (channel_ep_id, 1), + // (channel_b_id, 2), + // (channel_c_id, 3), + // (channel_d_id, 4), + // (channel_d_id, 3), + // (channel_ga_id, 3), + // (channel_ga_id, 1), + // ], + // ); - // Current shape for B: - // /---------\ - // /- ep -- b -- c -- d - // / \ - // mu ---------- ga - assert_channels_list_shape( - client_b.channel_store(), - cx_b, - &[ - (channel_mu_id, 0), - (channel_ep_id, 1), - (channel_b_id, 2), - (channel_c_id, 3), - (channel_d_id, 4), - (channel_d_id, 3), - (channel_ga_id, 3), - (channel_ga_id, 1), - ], - ); + // // Current shape for A: + // // /------\ + // // a - b -- c -- d + // // \-- ga + // assert_channels_list_shape( + // client_a.channel_store(), + // cx_a, + // &[ + // (channel_a_id, 0), + // (channel_b_id, 1), + // (channel_c_id, 2), + // (channel_d_id, 3), + // (channel_d_id, 2), + // (channel_ga_id, 2), + // ], + // ); - // Current shape for A: - // /------\ - // a - b -- c -- d - // \-- ga - assert_channels_list_shape( - client_a.channel_store(), - cx_a, - &[ - (channel_a_id, 0), - (channel_b_id, 1), - (channel_c_id, 2), - (channel_d_id, 3), - (channel_d_id, 2), - (channel_ga_id, 2), - ], - ); - - // Current shape for C: - // /-------\ - // ep -- b -- c -- d - // \-- ga - assert_channels_list_shape( - client_c.channel_store(), - cx_c, - &[ - (channel_ep_id, 0), - (channel_b_id, 1), - (channel_c_id, 2), - (channel_d_id, 3), - (channel_d_id, 2), - (channel_ga_id, 2), - ], - ); + // // Current shape for C: + // // /-------\ + // // ep -- b -- c -- d + // // \-- ga + // assert_channels_list_shape( + // client_c.channel_store(), + // cx_c, + // &[ + // (channel_ep_id, 0), + // (channel_b_id, 1), + // (channel_c_id, 2), + // (channel_d_id, 3), + // (channel_d_id, 2), + // (channel_ga_id, 2), + // ], + // ); } #[derive(Debug, PartialEq)] diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs index dcebe35b26..70ea87cfdd 100644 --- a/crates/collab_ui/src/collab_panel.rs +++ b/crates/collab_ui/src/collab_panel.rs @@ -120,22 +120,11 @@ struct StartLinkChannelFor { parent_id: Option, } -#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] -struct LinkChannel { - to: ChannelId, -} - #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] struct MoveChannel { to: ChannelId, } -#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] -struct UnlinkChannel { - channel_id: ChannelId, - parent_id: ChannelId, -} - type DraggedChannel = (Channel, Option); actions!( @@ -147,8 +136,7 @@ actions!( CollapseSelectedChannel, ExpandSelectedChannel, StartMoveChannel, - StartLinkChannel, - MoveOrLinkToSelected, + MoveSelected, InsertSpace, ] ); @@ -166,11 +154,8 @@ impl_actions!( JoinChannelCall, JoinChannelChat, CopyChannelLink, - LinkChannel, StartMoveChannelFor, - StartLinkChannelFor, MoveChannel, - UnlinkChannel, ToggleSelectedIx ] ); @@ -185,7 +170,7 @@ struct ChannelMoveClipboard { #[derive(Debug, Copy, Clone, PartialEq, Eq)] enum ClipboardIntent { Move, - Link, + // Link, } const COLLABORATION_PANEL_KEY: &'static str = "CollaborationPanel"; @@ -238,18 +223,6 @@ pub fn init(cx: &mut AppContext) { }, ); - cx.add_action( - |panel: &mut CollabPanel, - action: &StartLinkChannelFor, - _: &mut ViewContext| { - panel.channel_clipboard = Some(ChannelMoveClipboard { - channel_id: action.channel_id, - parent_id: action.parent_id, - intent: ClipboardIntent::Link, - }) - }, - ); - cx.add_action( |panel: &mut CollabPanel, _: &StartMoveChannel, _: &mut ViewContext| { if let Some((_, path)) = panel.selected_channel() { @@ -263,86 +236,51 @@ pub fn init(cx: &mut AppContext) { ); cx.add_action( - |panel: &mut CollabPanel, _: &StartLinkChannel, _: &mut ViewContext| { - if let Some((_, path)) = panel.selected_channel() { - panel.channel_clipboard = Some(ChannelMoveClipboard { - channel_id: path.channel_id(), - parent_id: path.parent_id(), - intent: ClipboardIntent::Link, - }) - } - }, - ); - - cx.add_action( - |panel: &mut CollabPanel, _: &MoveOrLinkToSelected, cx: &mut ViewContext| { + |panel: &mut CollabPanel, _: &MoveSelected, cx: &mut ViewContext| { let clipboard = panel.channel_clipboard.take(); if let Some(((selected_channel, _), clipboard)) = panel.selected_channel().zip(clipboard) { match clipboard.intent { - ClipboardIntent::Move if clipboard.parent_id.is_some() => { - let parent_id = clipboard.parent_id.unwrap(); - panel.channel_store.update(cx, |channel_store, cx| { - channel_store - .move_channel( - clipboard.channel_id, - parent_id, - selected_channel.id, - cx, - ) - .detach_and_log_err(cx) - }) - } - _ => panel.channel_store.update(cx, |channel_store, cx| { - channel_store - .link_channel(clipboard.channel_id, selected_channel.id, cx) - .detach_and_log_err(cx) + ClipboardIntent::Move => panel.channel_store.update(cx, |channel_store, cx| { + match clipboard.parent_id { + Some(parent_id) => channel_store.move_channel( + clipboard.channel_id, + parent_id, + selected_channel.id, + cx, + ), + None => channel_store.link_channel( + clipboard.channel_id, + selected_channel.id, + cx, + ), + } + .detach_and_log_err(cx) }), } } }, ); - cx.add_action( - |panel: &mut CollabPanel, action: &LinkChannel, cx: &mut ViewContext| { - if let Some(clipboard) = panel.channel_clipboard.take() { - panel.channel_store.update(cx, |channel_store, cx| { - channel_store - .link_channel(clipboard.channel_id, action.to, cx) - .detach_and_log_err(cx) - }) - } - }, - ); - cx.add_action( |panel: &mut CollabPanel, action: &MoveChannel, cx: &mut ViewContext| { if let Some(clipboard) = panel.channel_clipboard.take() { panel.channel_store.update(cx, |channel_store, cx| { - if let Some(parent) = clipboard.parent_id { - channel_store - .move_channel(clipboard.channel_id, parent, action.to, cx) - .detach_and_log_err(cx) - } else { - channel_store - .link_channel(clipboard.channel_id, action.to, cx) - .detach_and_log_err(cx) + match clipboard.parent_id { + Some(parent_id) => channel_store.move_channel( + clipboard.channel_id, + parent_id, + action.to, + cx, + ), + None => channel_store.link_channel(clipboard.channel_id, action.to, cx), } + .detach_and_log_err(cx) }) } }, ); - - cx.add_action( - |panel: &mut CollabPanel, action: &UnlinkChannel, cx: &mut ViewContext| { - panel.channel_store.update(cx, |channel_store, cx| { - channel_store - .unlink_channel(action.channel_id, action.parent_id, cx) - .detach_and_log_err(cx) - }) - }, - ); } #[derive(Debug)] @@ -2235,33 +2173,23 @@ impl CollabPanel { this.deploy_channel_context_menu(Some(e.position), &path, ix, cx); } }) - .on_up(MouseButton::Left, move |e, this, cx| { + .on_up(MouseButton::Left, move |_, this, cx| { if let Some((_, dragged_channel)) = cx .global::>() .currently_dragged::(cx.window()) { - if e.modifiers.alt { - this.channel_store.update(cx, |channel_store, cx| { - channel_store - .link_channel(dragged_channel.0.id, channel_id, cx) - .detach_and_log_err(cx) - }) - } else { - this.channel_store.update(cx, |channel_store, cx| { - match dragged_channel.1 { - Some(parent_id) => channel_store.move_channel( - dragged_channel.0.id, - parent_id, - channel_id, - cx, - ), - None => { - channel_store.link_channel(dragged_channel.0.id, channel_id, cx) - } - } - .detach_and_log_err(cx) - }) - } + this.channel_store.update(cx, |channel_store, cx| { + match dragged_channel.1 { + Some(parent_id) => channel_store.move_channel( + dragged_channel.0.id, + parent_id, + channel_id, + cx, + ), + None => channel_store.link_channel(dragged_channel.0.id, channel_id, cx), + } + .detach_and_log_err(cx) + }) } }) .on_move({ @@ -2288,18 +2216,10 @@ impl CollabPanel { }) .as_draggable( (channel.clone(), path.parent_id()), - move |modifiers, (channel, _), cx: &mut ViewContext| { + move |_, (channel, _), cx: &mut ViewContext| { let theme = &theme::current(cx).collab_panel; Flex::::row() - .with_children(modifiers.alt.then(|| { - Svg::new("icons/plus.svg") - .with_color(theme.channel_hash.color) - .constrained() - .with_width(theme.channel_hash.width) - .aligned() - .left() - })) .with_child( Svg::new("icons/hash.svg") .with_color(theme.channel_hash.color) @@ -2743,19 +2663,6 @@ impl CollabPanel { }, ), ContextMenuItem::Separator, - ]); - - if let Some(parent_id) = parent_id { - items.push(ContextMenuItem::action( - "Unlink from parent", - UnlinkChannel { - channel_id: path.channel_id(), - parent_id, - }, - )); - } - - items.extend([ ContextMenuItem::action( "Move this channel", StartMoveChannelFor { @@ -2763,13 +2670,6 @@ impl CollabPanel { parent_id, }, ), - ContextMenuItem::action( - "Link this channel", - StartLinkChannelFor { - channel_id: path.channel_id(), - parent_id, - }, - ), ]); if let Some(channel_name) = channel_name { @@ -2780,12 +2680,6 @@ impl CollabPanel { to: path.channel_id(), }, )); - items.push(ContextMenuItem::action( - format!("Link '#{}' here", channel_name), - LinkChannel { - to: path.channel_id(), - }, - )); } items.extend([ diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index 20a47954a4..1bf54dedb7 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -970,10 +970,16 @@ message UpdateChannels { repeated Channel channel_invitations = 5; repeated uint64 remove_channel_invitations = 6; repeated ChannelParticipants channel_participants = 7; + //repeated ChannelRoles channel_roles = 8; repeated UnseenChannelMessage unseen_channel_messages = 9; repeated UnseenChannelBufferChange unseen_channel_buffer_changes = 10; } +//message ChannelRoles { +// ChannelRole role = 1; +// uint64 channel_id = 2; +//} + message UnseenChannelMessage { uint64 channel_id = 1; uint64 message_id = 2;