diff --git a/crates/client/src/channel_store.rs b/crates/client/src/channel_store.rs index 1d3bbd4435..ee04865e50 100644 --- a/crates/client/src/channel_store.rs +++ b/crates/client/src/channel_store.rs @@ -26,6 +26,7 @@ pub struct Channel { pub id: ChannelId, pub name: String, pub parent_id: Option, + pub user_is_admin: bool, pub depth: usize, } @@ -247,6 +248,7 @@ impl ChannelStore { Arc::new(Channel { id: channel.id, name: channel.name, + user_is_admin: false, parent_id: None, depth: 0, }), @@ -267,6 +269,7 @@ impl ChannelStore { Arc::new(Channel { id: channel.id, name: channel.name, + user_is_admin: channel.user_is_admin, parent_id: Some(parent_id), depth, }), @@ -278,6 +281,7 @@ impl ChannelStore { Arc::new(Channel { id: channel.id, name: channel.name, + user_is_admin: channel.user_is_admin, parent_id: None, depth: 0, }), diff --git a/crates/client/src/channel_store_tests.rs b/crates/client/src/channel_store_tests.rs index 0d4ec6ce35..7f31243dad 100644 --- a/crates/client/src/channel_store_tests.rs +++ b/crates/client/src/channel_store_tests.rs @@ -18,11 +18,13 @@ fn test_update_channels(cx: &mut AppContext) { id: 1, name: "b".to_string(), parent_id: None, + user_is_admin: true, }, proto::Channel { id: 2, name: "a".to_string(), parent_id: None, + user_is_admin: false, }, ], ..Default::default() @@ -33,8 +35,8 @@ fn test_update_channels(cx: &mut AppContext) { &channel_store, &[ // - (0, "a"), - (0, "b"), + (0, "a", true), + (0, "b", false), ], cx, ); @@ -47,11 +49,13 @@ fn test_update_channels(cx: &mut AppContext) { id: 3, name: "x".to_string(), parent_id: Some(1), + user_is_admin: false, }, proto::Channel { id: 4, name: "y".to_string(), parent_id: Some(2), + user_is_admin: false, }, ], ..Default::default() @@ -61,11 +65,10 @@ fn test_update_channels(cx: &mut AppContext) { assert_channels( &channel_store, &[ - // - (0, "a"), - (1, "y"), - (0, "b"), - (1, "x"), + (0, "a", true), + (1, "y", true), + (0, "b", false), + (1, "x", false), ], cx, ); @@ -81,14 +84,14 @@ fn update_channels( fn assert_channels( channel_store: &ModelHandle, - expected_channels: &[(usize, &str)], + expected_channels: &[(usize, &str, bool)], cx: &AppContext, ) { channel_store.read_with(cx, |store, _| { let actual = store .channels() .iter() - .map(|c| (c.depth, c.name.as_str())) + .map(|c| (c.depth, c.name.as_str(), c.user_is_admin)) .collect::>(); assert_eq!(actual, expected_channels); }); diff --git a/crates/collab/src/db.rs b/crates/collab/src/db.rs index 5a2ab24b1e..6ebf5933df 100644 --- a/crates/collab/src/db.rs +++ b/crates/collab/src/db.rs @@ -3385,6 +3385,7 @@ impl Database { .map(|channel| Channel { id: channel.id, name: channel.name, + user_is_admin: false, parent_id: None, }) .collect(); @@ -3401,20 +3402,21 @@ impl Database { self.transaction(|tx| async move { let tx = tx; - let starting_channel_ids: Vec = channel_member::Entity::find() + let channel_memberships = channel_member::Entity::find() .filter( channel_member::Column::UserId .eq(user_id) .and(channel_member::Column::Accepted.eq(true)), ) - .select_only() - .column(channel_member::Column::ChannelId) - .into_values::<_, QueryChannelIds>() .all(&*tx) .await?; + let admin_channel_ids = channel_memberships + .iter() + .filter_map(|m| m.admin.then_some(m.channel_id)) + .collect::>(); let parents_by_child_id = self - .get_channel_descendants(starting_channel_ids, &*tx) + .get_channel_descendants(channel_memberships.iter().map(|m| m.channel_id), &*tx) .await?; let mut channels = Vec::with_capacity(parents_by_child_id.len()); @@ -3428,6 +3430,7 @@ impl Database { channels.push(Channel { id: row.id, name: row.name, + user_is_admin: admin_channel_ids.contains(&row.id), parent_id: parents_by_child_id.get(&row.id).copied().flatten(), }); } @@ -3627,7 +3630,7 @@ impl Database { r#" WITH RECURSIVE channel_tree(child_id, parent_id) AS ( SELECT root_ids.column1 as child_id, CAST(NULL as INTEGER) as parent_id - FROM (VALUES {}) as root_ids + FROM (VALUES {values}) as root_ids UNION SELECT channel_parents.child_id, channel_parents.parent_id FROM channel_parents, channel_tree @@ -3637,7 +3640,6 @@ impl Database { FROM channel_tree ORDER BY child_id, parent_id IS NOT NULL "#, - values ); #[derive(FromQueryResult, Debug, PartialEq)] @@ -3663,14 +3665,29 @@ impl Database { Ok(parents_by_child_id) } - pub async fn get_channel(&self, channel_id: ChannelId) -> Result> { + pub async fn get_channel( + &self, + channel_id: ChannelId, + user_id: UserId, + ) -> Result> { self.transaction(|tx| async move { let tx = tx; let channel = channel::Entity::find_by_id(channel_id).one(&*tx).await?; + let user_is_admin = channel_member::Entity::find() + .filter( + channel_member::Column::ChannelId + .eq(channel_id) + .and(channel_member::Column::UserId.eq(user_id)) + .and(channel_member::Column::Admin.eq(true)), + ) + .count(&*tx) + .await? + > 0; Ok(channel.map(|channel| Channel { id: channel.id, name: channel.name, + user_is_admin, parent_id: None, })) }) @@ -3942,6 +3959,7 @@ pub struct NewUserResult { pub struct Channel { pub id: ChannelId, pub name: String, + pub user_is_admin: bool, pub parent_id: Option, } @@ -4199,11 +4217,6 @@ pub struct WorktreeSettingsFile { pub content: String, } -#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] -enum QueryChannelIds { - ChannelId, -} - #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] enum QueryUserIds { UserId, diff --git a/crates/collab/src/db/tests.rs b/crates/collab/src/db/tests.rs index b4c22430e5..5ffcd12776 100644 --- a/crates/collab/src/db/tests.rs +++ b/crates/collab/src/db/tests.rs @@ -960,43 +960,50 @@ test_both_dbs!(test_channels_postgres, test_channels_sqlite, db, { id: zed_id, name: "zed".to_string(), parent_id: None, + user_is_admin: true, }, Channel { id: crdb_id, name: "crdb".to_string(), parent_id: Some(zed_id), + user_is_admin: true, }, Channel { id: livestreaming_id, name: "livestreaming".to_string(), parent_id: Some(zed_id), + user_is_admin: true, }, Channel { id: replace_id, name: "replace".to_string(), parent_id: Some(zed_id), + user_is_admin: true, }, Channel { id: rust_id, name: "rust".to_string(), parent_id: None, + user_is_admin: true, }, Channel { id: cargo_id, name: "cargo".to_string(), parent_id: Some(rust_id), + user_is_admin: true, }, Channel { id: cargo_ra_id, name: "cargo-ra".to_string(), parent_id: Some(cargo_id), + user_is_admin: true, } ] ); // Remove a single channel db.remove_channel(crdb_id, a_id).await.unwrap(); - assert!(db.get_channel(crdb_id).await.unwrap().is_none()); + 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(); @@ -1004,9 +1011,9 @@ test_both_dbs!(test_channels_postgres, test_channels_sqlite, db, { assert_eq!(channel_ids, &[rust_id, cargo_id, cargo_ra_id]); assert_eq!(user_ids, &[a_id]); - assert!(db.get_channel(rust_id).await.unwrap().is_none()); - assert!(db.get_channel(cargo_id).await.unwrap().is_none()); - assert!(db.get_channel(cargo_ra_id).await.unwrap().is_none()); + assert!(db.get_channel(rust_id, a_id).await.unwrap().is_none()); + assert!(db.get_channel(cargo_id, a_id).await.unwrap().is_none()); + assert!(db.get_channel(cargo_ra_id, a_id).await.unwrap().is_none()); }); test_both_dbs!( diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 17f1334544..31b0b2280a 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -2150,6 +2150,7 @@ async fn create_channel( id: id.to_proto(), name: request.name, parent_id: request.parent_id, + user_is_admin: true, }); if let Some(parent_id) = parent_id { @@ -2204,7 +2205,7 @@ async fn invite_channel_member( let db = session.db().await; let channel_id = ChannelId::from_proto(request.channel_id); let channel = db - .get_channel(channel_id) + .get_channel(channel_id, session.user_id) .await? .ok_or_else(|| anyhow!("channel not found"))?; let invitee_id = UserId::from_proto(request.user_id); @@ -2216,6 +2217,7 @@ async fn invite_channel_member( id: channel.id.to_proto(), name: channel.name, parent_id: None, + user_is_admin: false, }); for connection_id in session .connection_pool() @@ -2264,12 +2266,12 @@ async fn respond_to_channel_invite( ) -> Result<()> { let db = session.db().await; let channel_id = ChannelId::from_proto(request.channel_id); - let channel = db - .get_channel(channel_id) - .await? - .ok_or_else(|| anyhow!("no such channel"))?; db.respond_to_channel_invite(channel_id, session.user_id, request.accept) .await?; + let channel = db + .get_channel(channel_id, session.user_id) + .await? + .ok_or_else(|| anyhow!("no such channel"))?; let mut update = proto::UpdateChannels::default(); update @@ -2279,6 +2281,7 @@ async fn respond_to_channel_invite( update.channels.push(proto::Channel { id: channel.id.to_proto(), name: channel.name, + user_is_admin: channel.user_is_admin, parent_id: None, }); } @@ -2430,6 +2433,7 @@ fn build_initial_channels_update( update.channels.push(proto::Channel { id: channel.id.to_proto(), name: channel.name, + user_is_admin: channel.user_is_admin, parent_id: channel.parent_id.map(|id| id.to_proto()), }); } @@ -2447,6 +2451,7 @@ fn build_initial_channels_update( update.channel_invitations.push(proto::Channel { id: channel.id.to_proto(), name: channel.name, + user_is_admin: false, parent_id: None, }); } diff --git a/crates/collab/src/tests/channel_tests.rs b/crates/collab/src/tests/channel_tests.rs index b4f8477a2d..abaedb52a8 100644 --- a/crates/collab/src/tests/channel_tests.rs +++ b/crates/collab/src/tests/channel_tests.rs @@ -1,13 +1,10 @@ +use crate::tests::{room_participants, RoomParticipants, TestServer}; use call::ActiveCall; use client::{Channel, User}; use gpui::{executor::Deterministic, TestAppContext}; use rpc::proto; use std::sync::Arc; -use crate::tests::{room_participants, RoomParticipants}; - -use super::TestServer; - #[gpui::test] async fn test_basic_channels( deterministic: Arc, @@ -35,6 +32,7 @@ async fn test_basic_channels( id: channel_a_id, name: "channel-a".to_string(), parent_id: None, + user_is_admin: true, depth: 0, })] ) @@ -69,6 +67,7 @@ async fn test_basic_channels( id: channel_a_id, name: "channel-a".to_string(), parent_id: None, + user_is_admin: false, depth: 0, })] ) @@ -111,6 +110,7 @@ async fn test_basic_channels( id: channel_a_id, name: "channel-a".to_string(), parent_id: None, + user_is_admin: false, depth: 0, })] ) @@ -204,6 +204,7 @@ async fn test_channel_room( id: zed_id, name: "zed".to_string(), parent_id: None, + user_is_admin: false, depth: 0, })] ) diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index 602b34529e..7dd5a0a893 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -1295,7 +1295,8 @@ message Nonce { message Channel { uint64 id = 1; string name = 2; - optional uint64 parent_id = 3; + bool user_is_admin = 3; + optional uint64 parent_id = 4; } message Contact {