Push role refactoring through RPC/client

This commit is contained in:
Conrad Irwin 2023-10-11 21:57:46 -06:00
parent 690d9fb971
commit 540436a1f9
12 changed files with 178 additions and 89 deletions

View file

@ -3,4 +3,4 @@ xtask = "run --package xtask --"
[build] [build]
# v0 mangling scheme provides more detailed backtraces around closures # v0 mangling scheme provides more detailed backtraces around closures
rustflags = ["-C", "symbol-mangling-version=v0"] rustflags = ["-C", "symbol-mangling-version=v0", "-C", "link-arg=-fuse-ld=/opt/homebrew/Cellar/llvm/16.0.6/bin/ld64.lld"]

View file

@ -9,7 +9,7 @@ use db::RELEASE_CHANNEL;
use futures::{channel::mpsc, future::Shared, Future, FutureExt, StreamExt}; use futures::{channel::mpsc, future::Shared, Future, FutureExt, StreamExt};
use gpui::{AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, Task, WeakModelHandle}; use gpui::{AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, Task, WeakModelHandle};
use rpc::{ use rpc::{
proto::{self, ChannelEdge, ChannelPermission}, proto::{self, ChannelEdge, ChannelPermission, ChannelRole},
TypedEnvelope, TypedEnvelope,
}; };
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
@ -79,7 +79,7 @@ pub struct ChannelPath(Arc<[ChannelId]>);
pub struct ChannelMembership { pub struct ChannelMembership {
pub user: Arc<User>, pub user: Arc<User>,
pub kind: proto::channel_member::Kind, pub kind: proto::channel_member::Kind,
pub admin: bool, pub role: proto::ChannelRole,
} }
pub enum ChannelEvent { pub enum ChannelEvent {
@ -436,7 +436,7 @@ impl ChannelStore {
insert_edge: parent_edge, insert_edge: parent_edge,
channel_permissions: vec![ChannelPermission { channel_permissions: vec![ChannelPermission {
channel_id, channel_id,
is_admin: true, role: ChannelRole::Admin.into(),
}], }],
..Default::default() ..Default::default()
}, },
@ -512,7 +512,7 @@ impl ChannelStore {
&mut self, &mut self,
channel_id: ChannelId, channel_id: ChannelId,
user_id: UserId, user_id: UserId,
admin: bool, role: proto::ChannelRole,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) -> Task<Result<()>> { ) -> Task<Result<()>> {
if !self.outgoing_invites.insert((channel_id, user_id)) { if !self.outgoing_invites.insert((channel_id, user_id)) {
@ -526,7 +526,7 @@ impl ChannelStore {
.request(proto::InviteChannelMember { .request(proto::InviteChannelMember {
channel_id, channel_id,
user_id, user_id,
admin, role: role.into(),
}) })
.await; .await;
@ -570,11 +570,11 @@ impl ChannelStore {
}) })
} }
pub fn set_member_admin( pub fn set_member_role(
&mut self, &mut self,
channel_id: ChannelId, channel_id: ChannelId,
user_id: UserId, user_id: UserId,
admin: bool, role: proto::ChannelRole,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) -> Task<Result<()>> { ) -> Task<Result<()>> {
if !self.outgoing_invites.insert((channel_id, user_id)) { if !self.outgoing_invites.insert((channel_id, user_id)) {
@ -585,10 +585,10 @@ impl ChannelStore {
let client = self.client.clone(); let client = self.client.clone();
cx.spawn(|this, mut cx| async move { cx.spawn(|this, mut cx| async move {
let result = client let result = client
.request(proto::SetChannelMemberAdmin { .request(proto::SetChannelMemberRole {
channel_id, channel_id,
user_id, user_id,
admin, role: role.into(),
}) })
.await; .await;
@ -676,8 +676,8 @@ impl ChannelStore {
.filter_map(|(user, member)| { .filter_map(|(user, member)| {
Some(ChannelMembership { Some(ChannelMembership {
user, user,
admin: member.admin, role: member.role(),
kind: proto::channel_member::Kind::from_i32(member.kind)?, kind: member.kind(),
}) })
}) })
.collect()) .collect())
@ -935,7 +935,7 @@ impl ChannelStore {
} }
for permission in payload.channel_permissions { for permission in payload.channel_permissions {
if permission.is_admin { if permission.role() == proto::ChannelRole::Admin {
self.channels_with_admin_privileges self.channels_with_admin_privileges
.insert(permission.channel_id); .insert(permission.channel_id);
} else { } else {

View file

@ -26,7 +26,7 @@ fn test_update_channels(cx: &mut AppContext) {
], ],
channel_permissions: vec![proto::ChannelPermission { channel_permissions: vec![proto::ChannelPermission {
channel_id: 1, channel_id: 1,
is_admin: true, role: proto::ChannelRole::Admin.into(),
}], }],
..Default::default() ..Default::default()
}, },
@ -114,7 +114,7 @@ fn test_dangling_channel_paths(cx: &mut AppContext) {
], ],
channel_permissions: vec![proto::ChannelPermission { channel_permissions: vec![proto::ChannelPermission {
channel_id: 0, channel_id: 0,
is_admin: true, role: proto::ChannelRole::Admin.into(),
}], }],
..Default::default() ..Default::default()
}, },

View file

@ -1,4 +1,5 @@
use crate::Result; use crate::Result;
use rpc::proto;
use sea_orm::{entity::prelude::*, DbErr}; use sea_orm::{entity::prelude::*, DbErr};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -91,3 +92,30 @@ pub enum ChannelRole {
#[sea_orm(string_value = "guest")] #[sea_orm(string_value = "guest")]
Guest, Guest,
} }
impl From<proto::ChannelRole> for ChannelRole {
fn from(value: proto::ChannelRole) -> Self {
match value {
proto::ChannelRole::Admin => ChannelRole::Admin,
proto::ChannelRole::Member => ChannelRole::Member,
proto::ChannelRole::Guest => ChannelRole::Guest,
}
}
}
impl Into<proto::ChannelRole> for ChannelRole {
fn into(self) -> proto::ChannelRole {
match self {
ChannelRole::Admin => proto::ChannelRole::Admin,
ChannelRole::Member => proto::ChannelRole::Member,
ChannelRole::Guest => proto::ChannelRole::Guest,
}
}
}
impl Into<i32> for ChannelRole {
fn into(self) -> i32 {
let proto: proto::ChannelRole = self.into();
proto.into()
}
}

View file

@ -564,13 +564,18 @@ impl Database {
(false, true) => proto::channel_member::Kind::AncestorMember, (false, true) => proto::channel_member::Kind::AncestorMember,
(false, false) => continue, (false, false) => continue,
}; };
let channel_role = channel_role.unwrap_or(if is_admin {
ChannelRole::Admin
} else {
ChannelRole::Member
});
let user_id = user_id.to_proto(); let user_id = user_id.to_proto();
let kind = kind.into(); let kind = kind.into();
if let Some(last_row) = rows.last_mut() { if let Some(last_row) = rows.last_mut() {
if last_row.user_id == user_id { if last_row.user_id == user_id {
if is_direct_member { if is_direct_member {
last_row.kind = kind; last_row.kind = kind;
last_row.admin = channel_role == Some(ChannelRole::Admin) || is_admin; last_row.role = channel_role.into()
} }
continue; continue;
} }
@ -578,7 +583,7 @@ impl Database {
rows.push(proto::ChannelMember { rows.push(proto::ChannelMember {
user_id, user_id,
kind, kind,
admin: channel_role == Some(ChannelRole::Admin) || is_admin, role: channel_role.into(),
}); });
} }
@ -851,10 +856,11 @@ impl Database {
&self, &self,
user: UserId, user: UserId,
channel: ChannelId, channel: ChannelId,
to: ChannelId, new_parent: ChannelId,
tx: &DatabaseTransaction, tx: &DatabaseTransaction,
) -> Result<ChannelGraph> { ) -> Result<ChannelGraph> {
self.check_user_is_channel_admin(to, user, &*tx).await?; self.check_user_is_channel_admin(new_parent, user, &*tx)
.await?;
let paths = channel_path::Entity::find() let paths = channel_path::Entity::find()
.filter(channel_path::Column::IdPath.like(&format!("%/{}/%", channel))) .filter(channel_path::Column::IdPath.like(&format!("%/{}/%", channel)))
@ -872,7 +878,7 @@ impl Database {
} }
let paths_to_new_parent = channel_path::Entity::find() let paths_to_new_parent = channel_path::Entity::find()
.filter(channel_path::Column::ChannelId.eq(to)) .filter(channel_path::Column::ChannelId.eq(new_parent))
.all(tx) .all(tx)
.await?; .await?;
@ -906,7 +912,7 @@ impl Database {
if let Some(channel) = channel_descendants.get_mut(&channel) { if let Some(channel) = channel_descendants.get_mut(&channel) {
// Remove the other parents // Remove the other parents
channel.clear(); channel.clear();
channel.insert(to); channel.insert(new_parent);
} }
let channels = self let channels = self

View file

@ -328,17 +328,17 @@ async fn test_channel_invites(db: &Arc<Database>) {
proto::ChannelMember { proto::ChannelMember {
user_id: user_1.to_proto(), user_id: user_1.to_proto(),
kind: proto::channel_member::Kind::Member.into(), kind: proto::channel_member::Kind::Member.into(),
admin: true, role: proto::ChannelRole::Admin.into(),
}, },
proto::ChannelMember { proto::ChannelMember {
user_id: user_2.to_proto(), user_id: user_2.to_proto(),
kind: proto::channel_member::Kind::Invitee.into(), kind: proto::channel_member::Kind::Invitee.into(),
admin: false, role: proto::ChannelRole::Member.into(),
}, },
proto::ChannelMember { proto::ChannelMember {
user_id: user_3.to_proto(), user_id: user_3.to_proto(),
kind: proto::channel_member::Kind::Invitee.into(), kind: proto::channel_member::Kind::Invitee.into(),
admin: true, role: proto::ChannelRole::Admin.into(),
}, },
] ]
); );
@ -362,12 +362,12 @@ async fn test_channel_invites(db: &Arc<Database>) {
proto::ChannelMember { proto::ChannelMember {
user_id: user_1.to_proto(), user_id: user_1.to_proto(),
kind: proto::channel_member::Kind::Member.into(), kind: proto::channel_member::Kind::Member.into(),
admin: true, role: proto::ChannelRole::Admin.into(),
}, },
proto::ChannelMember { proto::ChannelMember {
user_id: user_2.to_proto(), user_id: user_2.to_proto(),
kind: proto::channel_member::Kind::AncestorMember.into(), kind: proto::channel_member::Kind::AncestorMember.into(),
admin: false, role: proto::ChannelRole::Member.into(),
}, },
] ]
); );

View file

@ -3,8 +3,8 @@ mod connection_pool;
use crate::{ use crate::{
auth, auth,
db::{ db::{
self, BufferId, ChannelId, ChannelRole, ChannelsForUser, Database, MessageId, ProjectId, self, BufferId, ChannelId, ChannelsForUser, Database, MessageId, ProjectId, RoomId,
RoomId, ServerId, User, UserId, ServerId, User, UserId,
}, },
executor::Executor, executor::Executor,
AppState, Result, AppState, Result,
@ -254,7 +254,7 @@ impl Server {
.add_request_handler(delete_channel) .add_request_handler(delete_channel)
.add_request_handler(invite_channel_member) .add_request_handler(invite_channel_member)
.add_request_handler(remove_channel_member) .add_request_handler(remove_channel_member)
.add_request_handler(set_channel_member_admin) .add_request_handler(set_channel_member_role)
.add_request_handler(rename_channel) .add_request_handler(rename_channel)
.add_request_handler(join_channel_buffer) .add_request_handler(join_channel_buffer)
.add_request_handler(leave_channel_buffer) .add_request_handler(leave_channel_buffer)
@ -2282,13 +2282,13 @@ async fn invite_channel_member(
let db = session.db().await; let db = session.db().await;
let channel_id = ChannelId::from_proto(request.channel_id); let channel_id = ChannelId::from_proto(request.channel_id);
let invitee_id = UserId::from_proto(request.user_id); let invitee_id = UserId::from_proto(request.user_id);
let role = if request.admin { db.invite_channel_member(
ChannelRole::Admin channel_id,
} else { invitee_id,
ChannelRole::Member session.user_id,
}; request.role().into(),
db.invite_channel_member(channel_id, invitee_id, session.user_id, role) )
.await?; .await?;
let (channel, _) = db let (channel, _) = db
.get_channel(channel_id, session.user_id) .get_channel(channel_id, session.user_id)
@ -2339,21 +2339,21 @@ async fn remove_channel_member(
Ok(()) Ok(())
} }
async fn set_channel_member_admin( async fn set_channel_member_role(
request: proto::SetChannelMemberAdmin, request: proto::SetChannelMemberRole,
response: Response<proto::SetChannelMemberAdmin>, response: Response<proto::SetChannelMemberRole>,
session: Session, session: Session,
) -> Result<()> { ) -> Result<()> {
let db = session.db().await; let db = session.db().await;
let channel_id = ChannelId::from_proto(request.channel_id); let channel_id = ChannelId::from_proto(request.channel_id);
let member_id = UserId::from_proto(request.user_id); let member_id = UserId::from_proto(request.user_id);
let role = if request.admin { db.set_channel_member_role(
ChannelRole::Admin channel_id,
} else { session.user_id,
ChannelRole::Member member_id,
}; request.role().into(),
db.set_channel_member_role(channel_id, session.user_id, member_id, role) )
.await?; .await?;
let (channel, has_accepted) = db let (channel, has_accepted) = db
.get_channel(channel_id, member_id) .get_channel(channel_id, member_id)
@ -2364,7 +2364,7 @@ async fn set_channel_member_admin(
if has_accepted { if has_accepted {
update.channel_permissions.push(proto::ChannelPermission { update.channel_permissions.push(proto::ChannelPermission {
channel_id: channel.id.to_proto(), channel_id: channel.id.to_proto(),
is_admin: request.admin, role: request.role,
}); });
} }
@ -2603,7 +2603,7 @@ async fn respond_to_channel_invite(
.into_iter() .into_iter()
.map(|channel_id| proto::ChannelPermission { .map(|channel_id| proto::ChannelPermission {
channel_id: channel_id.to_proto(), channel_id: channel_id.to_proto(),
is_admin: true, role: proto::ChannelRole::Admin.into(),
}), }),
); );
} }
@ -3106,7 +3106,7 @@ fn build_initial_channels_update(
.into_iter() .into_iter()
.map(|id| proto::ChannelPermission { .map(|id| proto::ChannelPermission {
channel_id: id.to_proto(), channel_id: id.to_proto(),
is_admin: true, role: proto::ChannelRole::Admin.into(),
}), }),
); );

View file

@ -68,7 +68,12 @@ async fn test_core_channels(
.update(cx_a, |store, cx| { .update(cx_a, |store, cx| {
assert!(!store.has_pending_channel_invite(channel_a_id, client_b.user_id().unwrap())); assert!(!store.has_pending_channel_invite(channel_a_id, client_b.user_id().unwrap()));
let invite = store.invite_member(channel_a_id, client_b.user_id().unwrap(), false, cx); let invite = store.invite_member(
channel_a_id,
client_b.user_id().unwrap(),
proto::ChannelRole::Member,
cx,
);
// Make sure we're synchronously storing the pending invite // Make sure we're synchronously storing the pending invite
assert!(store.has_pending_channel_invite(channel_a_id, client_b.user_id().unwrap())); assert!(store.has_pending_channel_invite(channel_a_id, client_b.user_id().unwrap()));
@ -103,12 +108,12 @@ async fn test_core_channels(
&[ &[
( (
client_a.user_id().unwrap(), client_a.user_id().unwrap(),
true, proto::ChannelRole::Admin,
proto::channel_member::Kind::Member, proto::channel_member::Kind::Member,
), ),
( (
client_b.user_id().unwrap(), client_b.user_id().unwrap(),
false, proto::ChannelRole::Member,
proto::channel_member::Kind::Invitee, proto::channel_member::Kind::Invitee,
), ),
], ],
@ -183,7 +188,12 @@ async fn test_core_channels(
client_a client_a
.channel_store() .channel_store()
.update(cx_a, |store, cx| { .update(cx_a, |store, cx| {
store.set_member_admin(channel_a_id, client_b.user_id().unwrap(), true, cx) store.set_member_role(
channel_a_id,
client_b.user_id().unwrap(),
proto::ChannelRole::Admin,
cx,
)
}) })
.await .await
.unwrap(); .unwrap();
@ -305,12 +315,12 @@ fn assert_participants_eq(participants: &[Arc<User>], expected_partitipants: &[u
#[track_caller] #[track_caller]
fn assert_members_eq( fn assert_members_eq(
members: &[ChannelMembership], members: &[ChannelMembership],
expected_members: &[(u64, bool, proto::channel_member::Kind)], expected_members: &[(u64, proto::ChannelRole, proto::channel_member::Kind)],
) { ) {
assert_eq!( assert_eq!(
members members
.iter() .iter()
.map(|member| (member.user.id, member.admin, member.kind)) .map(|member| (member.user.id, member.role, member.kind))
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
expected_members expected_members
); );
@ -611,7 +621,12 @@ async fn test_permissions_update_while_invited(
client_a client_a
.channel_store() .channel_store()
.update(cx_a, |channel_store, cx| { .update(cx_a, |channel_store, cx| {
channel_store.invite_member(rust_id, client_b.user_id().unwrap(), false, cx) channel_store.invite_member(
rust_id,
client_b.user_id().unwrap(),
proto::ChannelRole::Member,
cx,
)
}) })
.await .await
.unwrap(); .unwrap();
@ -634,7 +649,12 @@ async fn test_permissions_update_while_invited(
client_a client_a
.channel_store() .channel_store()
.update(cx_a, |channel_store, cx| { .update(cx_a, |channel_store, cx| {
channel_store.set_member_admin(rust_id, client_b.user_id().unwrap(), true, cx) channel_store.set_member_role(
rust_id,
client_b.user_id().unwrap(),
proto::ChannelRole::Admin,
cx,
)
}) })
.await .await
.unwrap(); .unwrap();
@ -803,7 +823,12 @@ async fn test_lost_channel_creation(
client_a client_a
.channel_store() .channel_store()
.update(cx_a, |channel_store, cx| { .update(cx_a, |channel_store, cx| {
channel_store.invite_member(channel_id, client_b.user_id().unwrap(), false, cx) channel_store.invite_member(
channel_id,
client_b.user_id().unwrap(),
proto::ChannelRole::Member,
cx,
)
}) })
.await .await
.unwrap(); .unwrap();

View file

@ -17,7 +17,7 @@ use gpui::{executor::Deterministic, ModelHandle, Task, TestAppContext, WindowHan
use language::LanguageRegistry; use language::LanguageRegistry;
use parking_lot::Mutex; use parking_lot::Mutex;
use project::{Project, WorktreeId}; use project::{Project, WorktreeId};
use rpc::RECEIVE_TIMEOUT; use rpc::{proto::ChannelRole, RECEIVE_TIMEOUT};
use settings::SettingsStore; use settings::SettingsStore;
use std::{ use std::{
cell::{Ref, RefCell, RefMut}, cell::{Ref, RefCell, RefMut},
@ -325,7 +325,7 @@ impl TestServer {
channel_store.invite_member( channel_store.invite_member(
channel_id, channel_id,
member_client.user_id().unwrap(), member_client.user_id().unwrap(),
false, ChannelRole::Member,
cx, cx,
) )
}) })
@ -613,7 +613,12 @@ impl TestClient {
cx_self cx_self
.read(ChannelStore::global) .read(ChannelStore::global)
.update(cx_self, |channel_store, cx| { .update(cx_self, |channel_store, cx| {
channel_store.invite_member(channel, other_client.user_id().unwrap(), true, cx) channel_store.invite_member(
channel,
other_client.user_id().unwrap(),
ChannelRole::Admin,
cx,
)
}) })
.await .await
.unwrap(); .unwrap();

View file

@ -1,5 +1,8 @@
use channel::{ChannelId, ChannelMembership, ChannelStore}; use channel::{ChannelId, ChannelMembership, ChannelStore};
use client::{proto, User, UserId, UserStore}; use client::{
proto::{self, ChannelRole},
User, UserId, UserStore,
};
use context_menu::{ContextMenu, ContextMenuItem}; use context_menu::{ContextMenu, ContextMenuItem};
use fuzzy::{match_strings, StringMatchCandidate}; use fuzzy::{match_strings, StringMatchCandidate};
use gpui::{ use gpui::{
@ -343,9 +346,11 @@ impl PickerDelegate for ChannelModalDelegate {
} }
fn confirm(&mut self, _: bool, cx: &mut ViewContext<Picker<Self>>) { fn confirm(&mut self, _: bool, cx: &mut ViewContext<Picker<Self>>) {
if let Some((selected_user, admin)) = self.user_at_index(self.selected_index) { if let Some((selected_user, role)) = self.user_at_index(self.selected_index) {
match self.mode { match self.mode {
Mode::ManageMembers => self.show_context_menu(admin.unwrap_or(false), cx), Mode::ManageMembers => {
self.show_context_menu(role.unwrap_or(ChannelRole::Member), cx)
}
Mode::InviteMembers => match self.member_status(selected_user.id, cx) { Mode::InviteMembers => match self.member_status(selected_user.id, cx) {
Some(proto::channel_member::Kind::Invitee) => { Some(proto::channel_member::Kind::Invitee) => {
self.remove_selected_member(cx); self.remove_selected_member(cx);
@ -373,7 +378,7 @@ impl PickerDelegate for ChannelModalDelegate {
let full_theme = &theme::current(cx); let full_theme = &theme::current(cx);
let theme = &full_theme.collab_panel.channel_modal; let theme = &full_theme.collab_panel.channel_modal;
let tabbed_modal = &full_theme.collab_panel.tabbed_modal; let tabbed_modal = &full_theme.collab_panel.tabbed_modal;
let (user, admin) = self.user_at_index(ix).unwrap(); let (user, role) = self.user_at_index(ix).unwrap();
let request_status = self.member_status(user.id, cx); let request_status = self.member_status(user.id, cx);
let style = tabbed_modal let style = tabbed_modal
@ -409,15 +414,25 @@ impl PickerDelegate for ChannelModalDelegate {
}, },
) )
}) })
.with_children(admin.and_then(|admin| { .with_children(if in_manage && role == Some(ChannelRole::Admin) {
(in_manage && admin).then(|| { Some(
Label::new("Admin", theme.member_tag.text.clone()) Label::new("Admin", theme.member_tag.text.clone())
.contained() .contained()
.with_style(theme.member_tag.container) .with_style(theme.member_tag.container)
.aligned() .aligned()
.left() .left(),
}) )
})) } else if in_manage && role == Some(ChannelRole::Guest) {
Some(
Label::new("Guest", theme.member_tag.text.clone())
.contained()
.with_style(theme.member_tag.container)
.aligned()
.left(),
)
} else {
None
})
.with_children({ .with_children({
let svg = match self.mode { let svg = match self.mode {
Mode::ManageMembers => Some( Mode::ManageMembers => Some(
@ -502,13 +517,13 @@ impl ChannelModalDelegate {
}) })
} }
fn user_at_index(&self, ix: usize) -> Option<(Arc<User>, Option<bool>)> { fn user_at_index(&self, ix: usize) -> Option<(Arc<User>, Option<ChannelRole>)> {
match self.mode { match self.mode {
Mode::ManageMembers => self.matching_member_indices.get(ix).and_then(|ix| { Mode::ManageMembers => self.matching_member_indices.get(ix).and_then(|ix| {
let channel_membership = self.members.get(*ix)?; let channel_membership = self.members.get(*ix)?;
Some(( Some((
channel_membership.user.clone(), channel_membership.user.clone(),
Some(channel_membership.admin), Some(channel_membership.role),
)) ))
}), }),
Mode::InviteMembers => Some((self.matching_users.get(ix).cloned()?, None)), Mode::InviteMembers => Some((self.matching_users.get(ix).cloned()?, None)),
@ -516,17 +531,21 @@ impl ChannelModalDelegate {
} }
fn toggle_selected_member_admin(&mut self, cx: &mut ViewContext<Picker<Self>>) -> Option<()> { fn toggle_selected_member_admin(&mut self, cx: &mut ViewContext<Picker<Self>>) -> Option<()> {
let (user, admin) = self.user_at_index(self.selected_index)?; let (user, role) = self.user_at_index(self.selected_index)?;
let admin = !admin.unwrap_or(false); let new_role = if role == Some(ChannelRole::Admin) {
ChannelRole::Member
} else {
ChannelRole::Admin
};
let update = self.channel_store.update(cx, |store, cx| { let update = self.channel_store.update(cx, |store, cx| {
store.set_member_admin(self.channel_id, user.id, admin, cx) store.set_member_role(self.channel_id, user.id, new_role, cx)
}); });
cx.spawn(|picker, mut cx| async move { cx.spawn(|picker, mut cx| async move {
update.await?; update.await?;
picker.update(&mut cx, |picker, cx| { picker.update(&mut cx, |picker, cx| {
let this = picker.delegate_mut(); let this = picker.delegate_mut();
if let Some(member) = this.members.iter_mut().find(|m| m.user.id == user.id) { if let Some(member) = this.members.iter_mut().find(|m| m.user.id == user.id) {
member.admin = admin; member.role = new_role;
} }
cx.focus_self(); cx.focus_self();
cx.notify(); cx.notify();
@ -572,7 +591,7 @@ impl ChannelModalDelegate {
fn invite_member(&mut self, user: Arc<User>, cx: &mut ViewContext<Picker<Self>>) { fn invite_member(&mut self, user: Arc<User>, cx: &mut ViewContext<Picker<Self>>) {
let invite_member = self.channel_store.update(cx, |store, cx| { let invite_member = self.channel_store.update(cx, |store, cx| {
store.invite_member(self.channel_id, user.id, false, cx) store.invite_member(self.channel_id, user.id, ChannelRole::Member, cx)
}); });
cx.spawn(|this, mut cx| async move { cx.spawn(|this, mut cx| async move {
@ -582,7 +601,7 @@ impl ChannelModalDelegate {
this.delegate_mut().members.push(ChannelMembership { this.delegate_mut().members.push(ChannelMembership {
user, user,
kind: proto::channel_member::Kind::Invitee, kind: proto::channel_member::Kind::Invitee,
admin: false, role: ChannelRole::Member,
}); });
cx.notify(); cx.notify();
}) })
@ -590,7 +609,7 @@ impl ChannelModalDelegate {
.detach_and_log_err(cx); .detach_and_log_err(cx);
} }
fn show_context_menu(&mut self, user_is_admin: bool, cx: &mut ViewContext<Picker<Self>>) { fn show_context_menu(&mut self, role: ChannelRole, cx: &mut ViewContext<Picker<Self>>) {
self.context_menu.update(cx, |context_menu, cx| { self.context_menu.update(cx, |context_menu, cx| {
context_menu.show( context_menu.show(
Default::default(), Default::default(),
@ -598,7 +617,7 @@ impl ChannelModalDelegate {
vec![ vec![
ContextMenuItem::action("Remove", RemoveMember), ContextMenuItem::action("Remove", RemoveMember),
ContextMenuItem::action( ContextMenuItem::action(
if user_is_admin { if role == ChannelRole::Admin {
"Make non-admin" "Make non-admin"
} else { } else {
"Make admin" "Make admin"

View file

@ -144,7 +144,7 @@ message Envelope {
DeleteChannel delete_channel = 118; DeleteChannel delete_channel = 118;
GetChannelMembers get_channel_members = 119; GetChannelMembers get_channel_members = 119;
GetChannelMembersResponse get_channel_members_response = 120; GetChannelMembersResponse get_channel_members_response = 120;
SetChannelMemberAdmin set_channel_member_admin = 121; SetChannelMemberRole set_channel_member_role = 145;
RenameChannel rename_channel = 122; RenameChannel rename_channel = 122;
RenameChannelResponse rename_channel_response = 123; RenameChannelResponse rename_channel_response = 123;
@ -170,7 +170,7 @@ message Envelope {
LinkChannel link_channel = 140; LinkChannel link_channel = 140;
UnlinkChannel unlink_channel = 141; UnlinkChannel unlink_channel = 141;
MoveChannel move_channel = 142; // current max: 144 MoveChannel move_channel = 142; // current max: 145
} }
} }
@ -979,7 +979,7 @@ message ChannelEdge {
message ChannelPermission { message ChannelPermission {
uint64 channel_id = 1; uint64 channel_id = 1;
bool is_admin = 2; ChannelRole role = 3;
} }
message ChannelParticipants { message ChannelParticipants {
@ -1005,8 +1005,8 @@ message GetChannelMembersResponse {
message ChannelMember { message ChannelMember {
uint64 user_id = 1; uint64 user_id = 1;
bool admin = 2;
Kind kind = 3; Kind kind = 3;
ChannelRole role = 4;
enum Kind { enum Kind {
Member = 0; Member = 0;
@ -1028,7 +1028,7 @@ message CreateChannelResponse {
message InviteChannelMember { message InviteChannelMember {
uint64 channel_id = 1; uint64 channel_id = 1;
uint64 user_id = 2; uint64 user_id = 2;
bool admin = 3; ChannelRole role = 4;
} }
message RemoveChannelMember { message RemoveChannelMember {
@ -1036,10 +1036,16 @@ message RemoveChannelMember {
uint64 user_id = 2; uint64 user_id = 2;
} }
message SetChannelMemberAdmin { enum ChannelRole {
Admin = 0;
Member = 1;
Guest = 2;
}
message SetChannelMemberRole {
uint64 channel_id = 1; uint64 channel_id = 1;
uint64 user_id = 2; uint64 user_id = 2;
bool admin = 3; ChannelRole role = 3;
} }
message RenameChannel { message RenameChannel {

View file

@ -230,7 +230,7 @@ messages!(
(SaveBuffer, Foreground), (SaveBuffer, Foreground),
(RenameChannel, Foreground), (RenameChannel, Foreground),
(RenameChannelResponse, Foreground), (RenameChannelResponse, Foreground),
(SetChannelMemberAdmin, Foreground), (SetChannelMemberRole, Foreground),
(SearchProject, Background), (SearchProject, Background),
(SearchProjectResponse, Background), (SearchProjectResponse, Background),
(ShareProject, Foreground), (ShareProject, Foreground),
@ -326,7 +326,7 @@ request_messages!(
(RemoveContact, Ack), (RemoveContact, Ack),
(RespondToContactRequest, Ack), (RespondToContactRequest, Ack),
(RespondToChannelInvite, Ack), (RespondToChannelInvite, Ack),
(SetChannelMemberAdmin, Ack), (SetChannelMemberRole, Ack),
(SendChannelMessage, SendChannelMessageResponse), (SendChannelMessage, SendChannelMessageResponse),
(GetChannelMessages, GetChannelMessagesResponse), (GetChannelMessages, GetChannelMessagesResponse),
(GetChannelMembers, GetChannelMembersResponse), (GetChannelMembers, GetChannelMembersResponse),