Add renames

co-authored-by: max <max@zed.dev>
This commit is contained in:
Mikayla 2023-08-09 12:20:48 -07:00
parent eed49a88bd
commit a3623ec2b8
No known key found for this signature in database
9 changed files with 356 additions and 120 deletions

View file

@ -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>,
}

View file

@ -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());

View file

@ -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,
});
}

View file

@ -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,
})],
);
});
}