mirror of
https://github.com/zed-industries/zed.git
synced 2024-09-19 02:17:35 +03:00
Add renames
co-authored-by: max <max@zed.dev>
This commit is contained in:
parent
eed49a88bd
commit
a3623ec2b8
@ -16,6 +16,7 @@ pub struct ChannelStore {
|
||||
channels: Vec<Arc<Channel>>,
|
||||
channel_invitations: Vec<Arc<Channel>>,
|
||||
channel_participants: HashMap<ChannelId, Vec<Arc<User>>>,
|
||||
channels_with_admin_privileges: HashSet<ChannelId>,
|
||||
outgoing_invites: HashSet<(ChannelId, UserId)>,
|
||||
client: Arc<Client>,
|
||||
user_store: ModelHandle<UserStore>,
|
||||
@ -28,7 +29,6 @@ pub struct Channel {
|
||||
pub id: ChannelId,
|
||||
pub name: String,
|
||||
pub parent_id: Option<ChannelId>,
|
||||
pub user_is_admin: bool,
|
||||
pub depth: usize,
|
||||
}
|
||||
|
||||
@ -79,6 +79,7 @@ impl ChannelStore {
|
||||
channels: vec![],
|
||||
channel_invitations: vec![],
|
||||
channel_participants: Default::default(),
|
||||
channels_with_admin_privileges: Default::default(),
|
||||
outgoing_invites: Default::default(),
|
||||
client,
|
||||
user_store,
|
||||
@ -100,17 +101,18 @@ impl ChannelStore {
|
||||
}
|
||||
|
||||
pub fn is_user_admin(&self, mut channel_id: ChannelId) -> bool {
|
||||
while let Some(channel) = self.channel_for_id(channel_id) {
|
||||
if channel.user_is_admin {
|
||||
loop {
|
||||
if self.channels_with_admin_privileges.contains(&channel_id) {
|
||||
return true;
|
||||
}
|
||||
if let Some(parent_id) = channel.parent_id {
|
||||
channel_id = parent_id;
|
||||
} else {
|
||||
break;
|
||||
if let Some(channel) = self.channel_for_id(channel_id) {
|
||||
if let Some(parent_id) = channel.parent_id {
|
||||
channel_id = parent_id;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn channel_participants(&self, channel_id: ChannelId) -> &[Arc<User>] {
|
||||
@ -228,6 +230,22 @@ impl ChannelStore {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn rename(
|
||||
&mut self,
|
||||
channel_id: ChannelId,
|
||||
new_name: &str,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Task<Result<()>> {
|
||||
let client = self.client.clone();
|
||||
let name = new_name.to_string();
|
||||
cx.spawn(|_this, _cx| async move {
|
||||
client
|
||||
.request(proto::RenameChannel { channel_id, name })
|
||||
.await?;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn respond_to_channel_invite(
|
||||
&mut self,
|
||||
channel_id: ChannelId,
|
||||
@ -315,6 +333,8 @@ impl ChannelStore {
|
||||
.retain(|channel| !payload.remove_channel_invitations.contains(&channel.id));
|
||||
self.channel_participants
|
||||
.retain(|channel_id, _| !payload.remove_channels.contains(channel_id));
|
||||
self.channels_with_admin_privileges
|
||||
.retain(|channel_id| !payload.remove_channels.contains(channel_id));
|
||||
|
||||
for channel in payload.channel_invitations {
|
||||
if let Some(existing_channel) = self
|
||||
@ -324,7 +344,6 @@ impl ChannelStore {
|
||||
{
|
||||
let existing_channel = Arc::make_mut(existing_channel);
|
||||
existing_channel.name = channel.name;
|
||||
existing_channel.user_is_admin = channel.user_is_admin;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -333,7 +352,6 @@ impl ChannelStore {
|
||||
Arc::new(Channel {
|
||||
id: channel.id,
|
||||
name: channel.name,
|
||||
user_is_admin: false,
|
||||
parent_id: None,
|
||||
depth: 0,
|
||||
}),
|
||||
@ -344,7 +362,6 @@ impl ChannelStore {
|
||||
if let Some(existing_channel) = self.channels.iter_mut().find(|c| c.id == channel.id) {
|
||||
let existing_channel = Arc::make_mut(existing_channel);
|
||||
existing_channel.name = channel.name;
|
||||
existing_channel.user_is_admin = channel.user_is_admin;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -357,7 +374,6 @@ impl ChannelStore {
|
||||
Arc::new(Channel {
|
||||
id: channel.id,
|
||||
name: channel.name,
|
||||
user_is_admin: channel.user_is_admin,
|
||||
parent_id: Some(parent_id),
|
||||
depth,
|
||||
}),
|
||||
@ -369,7 +385,6 @@ impl ChannelStore {
|
||||
Arc::new(Channel {
|
||||
id: channel.id,
|
||||
name: channel.name,
|
||||
user_is_admin: channel.user_is_admin,
|
||||
parent_id: None,
|
||||
depth: 0,
|
||||
}),
|
||||
@ -377,6 +392,16 @@ impl ChannelStore {
|
||||
}
|
||||
}
|
||||
|
||||
for permission in payload.channel_permissions {
|
||||
if permission.is_admin {
|
||||
self.channels_with_admin_privileges
|
||||
.insert(permission.channel_id);
|
||||
} else {
|
||||
self.channels_with_admin_privileges
|
||||
.remove(&permission.channel_id);
|
||||
}
|
||||
}
|
||||
|
||||
let mut all_user_ids = Vec::new();
|
||||
let channel_participants = payload.channel_participants;
|
||||
for entry in &channel_participants {
|
||||
|
@ -18,13 +18,11 @@ 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()
|
||||
@ -49,13 +47,11 @@ 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()
|
||||
@ -92,7 +88,7 @@ fn assert_channels(
|
||||
let actual = store
|
||||
.channels()
|
||||
.iter()
|
||||
.map(|c| (c.depth, c.name.as_str(), c.user_is_admin))
|
||||
.map(|c| (c.depth, c.name.as_str(), store.is_user_admin(c.id)))
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(actual, expected_channels);
|
||||
});
|
||||
|
@ -3155,7 +3155,7 @@ impl Database {
|
||||
live_kit_room: &str,
|
||||
creator_id: UserId,
|
||||
) -> Result<ChannelId> {
|
||||
let name = name.trim().trim_start_matches('#');
|
||||
let name = Self::sanitize_channel_name(name)?;
|
||||
self.transaction(move |tx| async move {
|
||||
if let Some(parent) = parent {
|
||||
self.check_user_is_channel_admin(parent, creator_id, &*tx)
|
||||
@ -3303,6 +3303,39 @@ impl Database {
|
||||
.await
|
||||
}
|
||||
|
||||
fn sanitize_channel_name(name: &str) -> Result<&str> {
|
||||
let new_name = name.trim().trim_start_matches('#');
|
||||
if new_name == "" {
|
||||
Err(anyhow!("channel name can't be blank"))?;
|
||||
}
|
||||
Ok(new_name)
|
||||
}
|
||||
|
||||
pub async fn rename_channel(
|
||||
&self,
|
||||
channel_id: ChannelId,
|
||||
user_id: UserId,
|
||||
new_name: &str,
|
||||
) -> Result<String> {
|
||||
self.transaction(move |tx| async move {
|
||||
let new_name = Self::sanitize_channel_name(new_name)?.to_string();
|
||||
|
||||
self.check_user_is_channel_admin(channel_id, user_id, &*tx)
|
||||
.await?;
|
||||
|
||||
channel::ActiveModel {
|
||||
id: ActiveValue::Unchanged(channel_id),
|
||||
name: ActiveValue::Set(new_name.clone()),
|
||||
..Default::default()
|
||||
}
|
||||
.update(&*tx)
|
||||
.await?;
|
||||
|
||||
Ok(new_name)
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn respond_to_channel_invite(
|
||||
&self,
|
||||
channel_id: ChannelId,
|
||||
@ -3400,7 +3433,6 @@ impl Database {
|
||||
.map(|channel| Channel {
|
||||
id: channel.id,
|
||||
name: channel.name,
|
||||
user_is_admin: false,
|
||||
parent_id: None,
|
||||
})
|
||||
.collect();
|
||||
@ -3426,10 +3458,6 @@ impl Database {
|
||||
.all(&*tx)
|
||||
.await?;
|
||||
|
||||
let admin_channel_ids = channel_memberships
|
||||
.iter()
|
||||
.filter_map(|m| m.admin.then_some(m.channel_id))
|
||||
.collect::<HashSet<_>>();
|
||||
let parents_by_child_id = self
|
||||
.get_channel_descendants(channel_memberships.iter().map(|m| m.channel_id), &*tx)
|
||||
.await?;
|
||||
@ -3445,7 +3473,6 @@ 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(),
|
||||
});
|
||||
}
|
||||
@ -3758,15 +3785,14 @@ impl Database {
|
||||
.one(&*tx)
|
||||
.await?;
|
||||
|
||||
let (user_is_admin, is_accepted) = channel_membership
|
||||
.map(|membership| (membership.admin, membership.accepted))
|
||||
.unwrap_or((false, false));
|
||||
let is_accepted = channel_membership
|
||||
.map(|membership| membership.accepted)
|
||||
.unwrap_or(false);
|
||||
|
||||
Ok(Some((
|
||||
Channel {
|
||||
id: channel.id,
|
||||
name: channel.name,
|
||||
user_is_admin,
|
||||
parent_id: None,
|
||||
},
|
||||
is_accepted,
|
||||
@ -4043,7 +4069,6 @@ pub struct NewUserResult {
|
||||
pub struct Channel {
|
||||
pub id: ChannelId,
|
||||
pub name: String,
|
||||
pub user_is_admin: bool,
|
||||
pub parent_id: Option<ChannelId>,
|
||||
}
|
||||
|
||||
|
@ -962,43 +962,36 @@ 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,
|
||||
}
|
||||
]
|
||||
);
|
||||
@ -1011,25 +1004,21 @@ test_both_dbs!(test_channels_postgres, test_channels_sqlite, db, {
|
||||
id: zed_id,
|
||||
name: "zed".to_string(),
|
||||
parent_id: None,
|
||||
user_is_admin: false,
|
||||
},
|
||||
Channel {
|
||||
id: crdb_id,
|
||||
name: "crdb".to_string(),
|
||||
parent_id: Some(zed_id),
|
||||
user_is_admin: false,
|
||||
},
|
||||
Channel {
|
||||
id: livestreaming_id,
|
||||
name: "livestreaming".to_string(),
|
||||
parent_id: Some(zed_id),
|
||||
user_is_admin: false,
|
||||
},
|
||||
Channel {
|
||||
id: replace_id,
|
||||
name: "replace".to_string(),
|
||||
parent_id: Some(zed_id),
|
||||
user_is_admin: false,
|
||||
},
|
||||
]
|
||||
);
|
||||
@ -1048,25 +1037,21 @@ 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: false,
|
||||
},
|
||||
Channel {
|
||||
id: livestreaming_id,
|
||||
name: "livestreaming".to_string(),
|
||||
parent_id: Some(zed_id),
|
||||
user_is_admin: false,
|
||||
},
|
||||
Channel {
|
||||
id: replace_id,
|
||||
name: "replace".to_string(),
|
||||
parent_id: Some(zed_id),
|
||||
user_is_admin: false,
|
||||
},
|
||||
]
|
||||
);
|
||||
@ -1296,6 +1281,66 @@ test_both_dbs!(
|
||||
}
|
||||
);
|
||||
|
||||
test_both_dbs!(
|
||||
test_channel_renames_postgres,
|
||||
test_channel_renames_sqlite,
|
||||
db,
|
||||
{
|
||||
db.create_server("test").await.unwrap();
|
||||
|
||||
let user_1 = db
|
||||
.create_user(
|
||||
"user1@example.com",
|
||||
false,
|
||||
NewUserParams {
|
||||
github_login: "user1".into(),
|
||||
github_user_id: 5,
|
||||
invite_count: 0,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.user_id;
|
||||
|
||||
let user_2 = db
|
||||
.create_user(
|
||||
"user2@example.com",
|
||||
false,
|
||||
NewUserParams {
|
||||
github_login: "user2".into(),
|
||||
github_user_id: 6,
|
||||
invite_count: 0,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.user_id;
|
||||
|
||||
let zed_id = db.create_root_channel("zed", "1", user_1).await.unwrap();
|
||||
|
||||
db.rename_channel(zed_id, user_1, "#zed-archive")
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let zed_archive_id = zed_id;
|
||||
|
||||
let (channel, _) = db
|
||||
.get_channel(zed_archive_id, user_1)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(channel.name, "zed-archive");
|
||||
|
||||
let non_permissioned_rename = db
|
||||
.rename_channel(zed_archive_id, user_2, "hacked-lol")
|
||||
.await;
|
||||
assert!(non_permissioned_rename.is_err());
|
||||
|
||||
let bad_name_rename = db.rename_channel(zed_id, user_1, "#").await;
|
||||
assert!(bad_name_rename.is_err())
|
||||
}
|
||||
);
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_multiple_signup_overwrite() {
|
||||
let test_db = TestDb::postgres(build_background_executor());
|
||||
|
@ -247,6 +247,7 @@ impl Server {
|
||||
.add_request_handler(invite_channel_member)
|
||||
.add_request_handler(remove_channel_member)
|
||||
.add_request_handler(set_channel_member_admin)
|
||||
.add_request_handler(rename_channel)
|
||||
.add_request_handler(get_channel_members)
|
||||
.add_request_handler(respond_to_channel_invite)
|
||||
.add_request_handler(join_channel)
|
||||
@ -2151,7 +2152,6 @@ async fn create_channel(
|
||||
id: id.to_proto(),
|
||||
name: request.name,
|
||||
parent_id: request.parent_id,
|
||||
user_is_admin: false,
|
||||
});
|
||||
|
||||
let user_ids_to_notify = if let Some(parent_id) = parent_id {
|
||||
@ -2165,7 +2165,10 @@ async fn create_channel(
|
||||
for connection_id in connection_pool.user_connection_ids(user_id) {
|
||||
let mut update = update.clone();
|
||||
if user_id == session.user_id {
|
||||
update.channels[0].user_is_admin = true;
|
||||
update.channel_permissions.push(proto::ChannelPermission {
|
||||
channel_id: id.to_proto(),
|
||||
is_admin: true,
|
||||
});
|
||||
}
|
||||
session.peer.send(connection_id, update)?;
|
||||
}
|
||||
@ -2224,7 +2227,6 @@ 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()
|
||||
@ -2283,18 +2285,9 @@ async fn set_channel_member_admin(
|
||||
|
||||
let mut update = proto::UpdateChannels::default();
|
||||
if has_accepted {
|
||||
update.channels.push(proto::Channel {
|
||||
id: channel.id.to_proto(),
|
||||
name: channel.name,
|
||||
parent_id: None,
|
||||
user_is_admin: request.admin,
|
||||
});
|
||||
} else {
|
||||
update.channel_invitations.push(proto::Channel {
|
||||
id: channel.id.to_proto(),
|
||||
name: channel.name,
|
||||
parent_id: None,
|
||||
user_is_admin: request.admin,
|
||||
update.channel_permissions.push(proto::ChannelPermission {
|
||||
channel_id: channel.id.to_proto(),
|
||||
is_admin: request.admin,
|
||||
});
|
||||
}
|
||||
|
||||
@ -2310,6 +2303,38 @@ async fn set_channel_member_admin(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn rename_channel(
|
||||
request: proto::RenameChannel,
|
||||
response: Response<proto::RenameChannel>,
|
||||
session: Session,
|
||||
) -> Result<()> {
|
||||
let db = session.db().await;
|
||||
let channel_id = ChannelId::from_proto(request.channel_id);
|
||||
let new_name = db
|
||||
.rename_channel(channel_id, session.user_id, &request.name)
|
||||
.await?;
|
||||
|
||||
response.send(proto::Ack {})?;
|
||||
|
||||
let mut update = proto::UpdateChannels::default();
|
||||
update.channels.push(proto::Channel {
|
||||
id: request.channel_id,
|
||||
name: new_name,
|
||||
parent_id: None,
|
||||
});
|
||||
|
||||
let member_ids = db.get_channel_members(channel_id).await?;
|
||||
|
||||
let connection_pool = session.connection_pool().await;
|
||||
for member_id in member_ids {
|
||||
for connection_id in connection_pool.user_connection_ids(member_id) {
|
||||
session.peer.send(connection_id, update.clone())?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_channel_members(
|
||||
request: proto::GetChannelMembers,
|
||||
response: Response<proto::GetChannelMembers>,
|
||||
@ -2345,7 +2370,6 @@ async fn respond_to_channel_invite(
|
||||
.extend(channels.into_iter().map(|channel| proto::Channel {
|
||||
id: channel.id.to_proto(),
|
||||
name: channel.name,
|
||||
user_is_admin: channel.user_is_admin,
|
||||
parent_id: channel.parent_id.map(ChannelId::to_proto),
|
||||
}));
|
||||
update
|
||||
@ -2505,7 +2529,6 @@ 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()),
|
||||
});
|
||||
}
|
||||
@ -2523,7 +2546,6 @@ 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,
|
||||
});
|
||||
}
|
||||
|
@ -40,14 +40,12 @@ async fn test_core_channels(
|
||||
id: channel_a_id,
|
||||
name: "channel-a".to_string(),
|
||||
parent_id: None,
|
||||
user_is_admin: true,
|
||||
depth: 0,
|
||||
}),
|
||||
Arc::new(Channel {
|
||||
id: channel_b_id,
|
||||
name: "channel-b".to_string(),
|
||||
parent_id: Some(channel_a_id),
|
||||
user_is_admin: true,
|
||||
depth: 1,
|
||||
})
|
||||
]
|
||||
@ -82,7 +80,6 @@ async fn test_core_channels(
|
||||
id: channel_a_id,
|
||||
name: "channel-a".to_string(),
|
||||
parent_id: None,
|
||||
user_is_admin: false,
|
||||
depth: 0,
|
||||
})]
|
||||
)
|
||||
@ -131,14 +128,13 @@ async fn test_core_channels(
|
||||
id: channel_a_id,
|
||||
name: "channel-a".to_string(),
|
||||
parent_id: None,
|
||||
user_is_admin: false,
|
||||
|
||||
depth: 0,
|
||||
}),
|
||||
Arc::new(Channel {
|
||||
id: channel_b_id,
|
||||
name: "channel-b".to_string(),
|
||||
parent_id: Some(channel_a_id),
|
||||
user_is_admin: false,
|
||||
depth: 1,
|
||||
})
|
||||
]
|
||||
@ -162,21 +158,18 @@ async fn test_core_channels(
|
||||
id: channel_a_id,
|
||||
name: "channel-a".to_string(),
|
||||
parent_id: None,
|
||||
user_is_admin: false,
|
||||
depth: 0,
|
||||
}),
|
||||
Arc::new(Channel {
|
||||
id: channel_b_id,
|
||||
name: "channel-b".to_string(),
|
||||
parent_id: Some(channel_a_id),
|
||||
user_is_admin: false,
|
||||
depth: 1,
|
||||
}),
|
||||
Arc::new(Channel {
|
||||
id: channel_c_id,
|
||||
name: "channel-c".to_string(),
|
||||
parent_id: Some(channel_b_id),
|
||||
user_is_admin: false,
|
||||
depth: 2,
|
||||
}),
|
||||
]
|
||||
@ -204,21 +197,18 @@ async fn test_core_channels(
|
||||
id: channel_a_id,
|
||||
name: "channel-a".to_string(),
|
||||
parent_id: None,
|
||||
user_is_admin: true,
|
||||
depth: 0,
|
||||
}),
|
||||
Arc::new(Channel {
|
||||
id: channel_b_id,
|
||||
name: "channel-b".to_string(),
|
||||
parent_id: Some(channel_a_id),
|
||||
user_is_admin: false,
|
||||
depth: 1,
|
||||
}),
|
||||
Arc::new(Channel {
|
||||
id: channel_c_id,
|
||||
name: "channel-c".to_string(),
|
||||
parent_id: Some(channel_b_id),
|
||||
user_is_admin: false,
|
||||
depth: 2,
|
||||
}),
|
||||
]
|
||||
@ -244,7 +234,7 @@ async fn test_core_channels(
|
||||
id: channel_a_id,
|
||||
name: "channel-a".to_string(),
|
||||
parent_id: None,
|
||||
user_is_admin: true,
|
||||
|
||||
depth: 0,
|
||||
})]
|
||||
)
|
||||
@ -256,7 +246,7 @@ async fn test_core_channels(
|
||||
id: channel_a_id,
|
||||
name: "channel-a".to_string(),
|
||||
parent_id: None,
|
||||
user_is_admin: true,
|
||||
|
||||
depth: 0,
|
||||
})]
|
||||
)
|
||||
@ -281,7 +271,6 @@ async fn test_core_channels(
|
||||
id: channel_a_id,
|
||||
name: "channel-a".to_string(),
|
||||
parent_id: None,
|
||||
user_is_admin: true,
|
||||
depth: 0,
|
||||
})]
|
||||
)
|
||||
@ -395,7 +384,6 @@ async fn test_channel_room(
|
||||
id: zed_id,
|
||||
name: "zed".to_string(),
|
||||
parent_id: None,
|
||||
user_is_admin: false,
|
||||
depth: 0,
|
||||
})]
|
||||
)
|
||||
@ -617,7 +605,7 @@ async fn test_permissions_update_while_invited(
|
||||
id: rust_id,
|
||||
name: "rust".to_string(),
|
||||
parent_id: None,
|
||||
user_is_admin: false,
|
||||
|
||||
depth: 0,
|
||||
})],
|
||||
);
|
||||
@ -643,7 +631,7 @@ async fn test_permissions_update_while_invited(
|
||||
id: rust_id,
|
||||
name: "rust".to_string(),
|
||||
parent_id: None,
|
||||
user_is_admin: true,
|
||||
|
||||
depth: 0,
|
||||
})],
|
||||
);
|
||||
@ -651,3 +639,59 @@ async fn test_permissions_update_while_invited(
|
||||
assert_eq!(channels.channels(), &[],);
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_channel_rename(
|
||||
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_a, "user_a").await;
|
||||
let client_b = server.create_client(cx_b, "user_b").await;
|
||||
|
||||
let rust_id = server
|
||||
.make_channel("rust", (&client_a, cx_a), &mut [(&client_b, cx_b)])
|
||||
.await;
|
||||
|
||||
// Rename the channel
|
||||
client_a
|
||||
.channel_store()
|
||||
.update(cx_a, |channel_store, cx| {
|
||||
channel_store.rename(rust_id, "#rust-archive", cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let rust_archive_id = rust_id;
|
||||
deterministic.run_until_parked();
|
||||
|
||||
// Client A sees the channel with its new name.
|
||||
client_a.channel_store().read_with(cx_a, |channels, _| {
|
||||
assert_eq!(
|
||||
channels.channels(),
|
||||
&[Arc::new(Channel {
|
||||
id: rust_archive_id,
|
||||
name: "rust-archive".to_string(),
|
||||
parent_id: None,
|
||||
|
||||
depth: 0,
|
||||
})],
|
||||
);
|
||||
});
|
||||
|
||||
// Client B sees the channel with its new name.
|
||||
client_b.channel_store().read_with(cx_b, |channels, _| {
|
||||
assert_eq!(
|
||||
channels.channels(),
|
||||
&[Arc::new(Channel {
|
||||
id: rust_archive_id,
|
||||
name: "rust-archive".to_string(),
|
||||
parent_id: None,
|
||||
|
||||
depth: 0,
|
||||
})],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -64,11 +64,22 @@ struct ManageMembers {
|
||||
channel_id: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
struct RenameChannel {
|
||||
channel_id: u64,
|
||||
}
|
||||
|
||||
actions!(collab_panel, [ToggleFocus, Remove, Secondary]);
|
||||
|
||||
impl_actions!(
|
||||
collab_panel,
|
||||
[RemoveChannel, NewChannel, InviteMembers, ManageMembers]
|
||||
[
|
||||
RemoveChannel,
|
||||
NewChannel,
|
||||
InviteMembers,
|
||||
ManageMembers,
|
||||
RenameChannel
|
||||
]
|
||||
);
|
||||
|
||||
const CHANNELS_PANEL_KEY: &'static str = "ChannelsPanel";
|
||||
@ -83,16 +94,19 @@ pub fn init(_client: Arc<Client>, cx: &mut AppContext) {
|
||||
cx.add_action(CollabPanel::select_prev);
|
||||
cx.add_action(CollabPanel::confirm);
|
||||
cx.add_action(CollabPanel::remove);
|
||||
cx.add_action(CollabPanel::remove_channel_action);
|
||||
cx.add_action(CollabPanel::remove_selected_channel);
|
||||
cx.add_action(CollabPanel::show_inline_context_menu);
|
||||
cx.add_action(CollabPanel::new_subchannel);
|
||||
cx.add_action(CollabPanel::invite_members);
|
||||
cx.add_action(CollabPanel::manage_members);
|
||||
cx.add_action(CollabPanel::rename_selected_channel);
|
||||
cx.add_action(CollabPanel::rename_channel);
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ChannelEditingState {
|
||||
parent_id: Option<u64>,
|
||||
#[derive(Debug)]
|
||||
pub enum ChannelEditingState {
|
||||
Create { parent_id: Option<u64> },
|
||||
Rename { channel_id: u64 },
|
||||
}
|
||||
|
||||
pub struct CollabPanel {
|
||||
@ -581,19 +595,32 @@ impl CollabPanel {
|
||||
executor.clone(),
|
||||
));
|
||||
if let Some(state) = &self.channel_editing_state {
|
||||
if state.parent_id.is_none() {
|
||||
if matches!(state, ChannelEditingState::Create { parent_id: None }) {
|
||||
self.entries.push(ListEntry::ChannelEditor { depth: 0 });
|
||||
}
|
||||
}
|
||||
for mat in matches {
|
||||
let channel = &channels[mat.candidate_id];
|
||||
self.entries.push(ListEntry::Channel(channel.clone()));
|
||||
if let Some(state) = &self.channel_editing_state {
|
||||
if state.parent_id == Some(channel.id) {
|
||||
|
||||
match &self.channel_editing_state {
|
||||
Some(ChannelEditingState::Create { parent_id })
|
||||
if *parent_id == Some(channel.id) =>
|
||||
{
|
||||
self.entries.push(ListEntry::Channel(channel.clone()));
|
||||
self.entries.push(ListEntry::ChannelEditor {
|
||||
depth: channel.depth + 1,
|
||||
});
|
||||
}
|
||||
Some(ChannelEditingState::Rename { channel_id })
|
||||
if *channel_id == channel.id =>
|
||||
{
|
||||
self.entries.push(ListEntry::ChannelEditor {
|
||||
depth: channel.depth + 1,
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
self.entries.push(ListEntry::Channel(channel.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1065,15 +1092,15 @@ impl CollabPanel {
|
||||
&mut self,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Option<(ChannelEditingState, String)> {
|
||||
let result = self
|
||||
.channel_editing_state
|
||||
.take()
|
||||
.map(|state| (state, self.channel_name_editor.read(cx).text(cx)));
|
||||
|
||||
self.channel_name_editor
|
||||
.update(cx, |editor, cx| editor.set_text("", cx));
|
||||
|
||||
result
|
||||
if let Some(state) = self.channel_editing_state.take() {
|
||||
self.channel_name_editor.update(cx, |editor, cx| {
|
||||
let name = editor.text(cx);
|
||||
editor.set_text("", cx);
|
||||
Some((state, name))
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn render_header(
|
||||
@ -1646,6 +1673,7 @@ impl CollabPanel {
|
||||
ContextMenuItem::action("Remove Channel", RemoveChannel { channel_id }),
|
||||
ContextMenuItem::action("Manage members", ManageMembers { channel_id }),
|
||||
ContextMenuItem::action("Invite members", InviteMembers { channel_id }),
|
||||
ContextMenuItem::action("Rename Channel", RenameChannel { channel_id }),
|
||||
],
|
||||
cx,
|
||||
);
|
||||
@ -1702,6 +1730,10 @@ impl CollabPanel {
|
||||
}
|
||||
|
||||
fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext<Self>) {
|
||||
if self.confirm_channel_edit(cx) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(selection) = self.selection {
|
||||
if let Some(entry) = self.entries.get(selection) {
|
||||
match entry {
|
||||
@ -1747,30 +1779,38 @@ impl CollabPanel {
|
||||
ListEntry::Channel(channel) => {
|
||||
self.join_channel(channel.id, cx);
|
||||
}
|
||||
ListEntry::ChannelEditor { .. } => {
|
||||
self.confirm_channel_edit(cx);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.confirm_channel_edit(cx);
|
||||
}
|
||||
}
|
||||
|
||||
fn confirm_channel_edit(&mut self, cx: &mut ViewContext<'_, '_, CollabPanel>) {
|
||||
fn confirm_channel_edit(&mut self, cx: &mut ViewContext<'_, '_, CollabPanel>) -> bool {
|
||||
if let Some((editing_state, channel_name)) = self.take_editing_state(cx) {
|
||||
let create_channel = self.channel_store.update(cx, |channel_store, _| {
|
||||
channel_store.create_channel(&channel_name, editing_state.parent_id)
|
||||
});
|
||||
|
||||
match editing_state {
|
||||
ChannelEditingState::Create { parent_id } => {
|
||||
let request = self.channel_store.update(cx, |channel_store, _| {
|
||||
channel_store.create_channel(&channel_name, parent_id)
|
||||
});
|
||||
cx.foreground()
|
||||
.spawn(async move {
|
||||
request.await?;
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
ChannelEditingState::Rename { channel_id } => {
|
||||
self.channel_store
|
||||
.update(cx, |channel_store, cx| {
|
||||
channel_store.rename(channel_id, &channel_name, cx)
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
}
|
||||
self.update_entries(false, cx);
|
||||
|
||||
cx.foreground()
|
||||
.spawn(async move {
|
||||
create_channel.await.log_err();
|
||||
})
|
||||
.detach();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@ -1804,14 +1844,14 @@ impl CollabPanel {
|
||||
}
|
||||
|
||||
fn new_root_channel(&mut self, cx: &mut ViewContext<Self>) {
|
||||
self.channel_editing_state = Some(ChannelEditingState { parent_id: None });
|
||||
self.channel_editing_state = Some(ChannelEditingState::Create { parent_id: None });
|
||||
self.update_entries(true, cx);
|
||||
cx.focus(self.channel_name_editor.as_any());
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn new_subchannel(&mut self, action: &NewChannel, cx: &mut ViewContext<Self>) {
|
||||
self.channel_editing_state = Some(ChannelEditingState {
|
||||
self.channel_editing_state = Some(ChannelEditingState::Create {
|
||||
parent_id: Some(action.channel_id),
|
||||
});
|
||||
self.update_entries(true, cx);
|
||||
@ -1835,7 +1875,33 @@ impl CollabPanel {
|
||||
}
|
||||
}
|
||||
|
||||
fn rename(&mut self, _: &menu::SecondaryConfirm, cx: &mut ViewContext<Self>) {}
|
||||
fn rename_selected_channel(&mut self, _: &menu::SecondaryConfirm, cx: &mut ViewContext<Self>) {
|
||||
if let Some(channel) = self.selected_channel() {
|
||||
self.rename_channel(
|
||||
&RenameChannel {
|
||||
channel_id: channel.id,
|
||||
},
|
||||
cx,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn rename_channel(&mut self, action: &RenameChannel, cx: &mut ViewContext<Self>) {
|
||||
if let Some(channel) = self
|
||||
.channel_store
|
||||
.read(cx)
|
||||
.channel_for_id(action.channel_id)
|
||||
{
|
||||
self.channel_editing_state = Some(ChannelEditingState::Rename {
|
||||
channel_id: action.channel_id,
|
||||
});
|
||||
self.channel_name_editor.update(cx, |editor, cx| {
|
||||
editor.set_text(channel.name.clone(), cx);
|
||||
editor.select_all(&Default::default(), cx);
|
||||
});
|
||||
self.update_entries(true, cx);
|
||||
}
|
||||
}
|
||||
|
||||
fn show_inline_context_menu(&mut self, _: &menu::ShowContextMenu, cx: &mut ViewContext<Self>) {
|
||||
let Some(channel) = self.selected_channel() else {
|
||||
@ -1887,7 +1953,7 @@ impl CollabPanel {
|
||||
.detach();
|
||||
}
|
||||
|
||||
fn remove_channel_action(&mut self, action: &RemoveChannel, cx: &mut ViewContext<Self>) {
|
||||
fn remove_selected_channel(&mut self, action: &RemoveChannel, cx: &mut ViewContext<Self>) {
|
||||
self.remove_channel(action.channel_id, cx)
|
||||
}
|
||||
|
||||
|
@ -141,6 +141,7 @@ message Envelope {
|
||||
GetChannelMembers get_channel_members = 128;
|
||||
GetChannelMembersResponse get_channel_members_response = 129;
|
||||
SetChannelMemberAdmin set_channel_member_admin = 130;
|
||||
RenameChannel rename_channel = 131;
|
||||
}
|
||||
}
|
||||
|
||||
@ -874,6 +875,12 @@ message UpdateChannels {
|
||||
repeated Channel channel_invitations = 3;
|
||||
repeated uint64 remove_channel_invitations = 4;
|
||||
repeated ChannelParticipants channel_participants = 5;
|
||||
repeated ChannelPermission channel_permissions = 6;
|
||||
}
|
||||
|
||||
message ChannelPermission {
|
||||
uint64 channel_id = 1;
|
||||
bool is_admin = 2;
|
||||
}
|
||||
|
||||
message ChannelParticipants {
|
||||
@ -935,6 +942,11 @@ message SetChannelMemberAdmin {
|
||||
bool admin = 3;
|
||||
}
|
||||
|
||||
message RenameChannel {
|
||||
uint64 channel_id = 1;
|
||||
string name = 2;
|
||||
}
|
||||
|
||||
message RespondToChannelInvite {
|
||||
uint64 channel_id = 1;
|
||||
bool accept = 2;
|
||||
@ -1303,8 +1315,7 @@ message Nonce {
|
||||
message Channel {
|
||||
uint64 id = 1;
|
||||
string name = 2;
|
||||
bool user_is_admin = 3;
|
||||
optional uint64 parent_id = 4;
|
||||
optional uint64 parent_id = 3;
|
||||
}
|
||||
|
||||
message Contact {
|
||||
|
@ -217,6 +217,7 @@ messages!(
|
||||
(JoinChannel, Foreground),
|
||||
(RoomUpdated, Foreground),
|
||||
(SaveBuffer, Foreground),
|
||||
(RenameChannel, Foreground),
|
||||
(SetChannelMemberAdmin, Foreground),
|
||||
(SearchProject, Background),
|
||||
(SearchProjectResponse, Background),
|
||||
@ -304,6 +305,7 @@ request_messages!(
|
||||
(JoinChannel, JoinRoomResponse),
|
||||
(RemoveChannel, Ack),
|
||||
(RenameProjectEntry, ProjectEntryResponse),
|
||||
(RenameChannel, Ack),
|
||||
(SaveBuffer, BufferSaved),
|
||||
(SearchProject, SearchProjectResponse),
|
||||
(ShareProject, ShareProjectResponse),
|
||||
|
Loading…
Reference in New Issue
Block a user