Detailed changes
@@ -123,6 +123,7 @@ impl Channel {
}
}
+#[derive(Debug)]
pub struct ChannelMembership {
pub user: Arc<User>,
pub kind: proto::channel_member::Kind,
@@ -815,9 +816,11 @@ impl ChannelStore {
Ok(())
})
}
- pub fn get_channel_member_details(
+ pub fn fuzzy_search_members(
&self,
channel_id: ChannelId,
+ query: String,
+ limit: u16,
cx: &mut ModelContext<Self>,
) -> Task<Result<Vec<ChannelMembership>>> {
let client = self.client.clone();
@@ -826,26 +829,24 @@ impl ChannelStore {
let response = client
.request(proto::GetChannelMembers {
channel_id: channel_id.0,
+ query,
+ limit: limit as u64,
})
.await?;
-
- let user_ids = response.members.iter().map(|m| m.user_id).collect();
- let user_store = user_store
- .upgrade()
- .ok_or_else(|| anyhow!("user store dropped"))?;
- let users = user_store
- .update(&mut cx, |user_store, cx| user_store.get_users(user_ids, cx))?
- .await?;
-
- Ok(users
- .into_iter()
- .zip(response.members)
- .map(|(user, member)| ChannelMembership {
- user,
- role: member.role(),
- kind: member.kind(),
- })
- .collect())
+ user_store.update(&mut cx, |user_store, _| {
+ user_store.insert(response.users);
+ response
+ .members
+ .into_iter()
+ .filter_map(|member| {
+ Some(ChannelMembership {
+ user: user_store.get_cached_user(member.user_id)?,
+ role: member.role(),
+ kind: member.kind(),
+ })
+ })
+ .collect()
+ })
})
}
@@ -85,7 +85,7 @@ lazy_static! {
}
pub const INITIAL_RECONNECTION_DELAY: Duration = Duration::from_millis(100);
-pub const CONNECTION_TIMEOUT: Duration = Duration::from_secs(5);
+pub const CONNECTION_TIMEOUT: Duration = Duration::from_secs(20);
actions!(client, [SignIn, SignOut, Reconnect]);
@@ -670,28 +670,31 @@ impl UserStore {
cx.spawn(|this, mut cx| async move {
if let Some(rpc) = client.upgrade() {
let response = rpc.request(request).await.context("error loading users")?;
- let users = response
- .users
- .into_iter()
- .map(User::new)
- .collect::<Vec<_>>();
-
- this.update(&mut cx, |this, _| {
- for user in &users {
- this.users.insert(user.id, user.clone());
- this.by_github_login
- .insert(user.github_login.clone(), user.id);
- }
- })
- .ok();
+ let users = response.users;
- Ok(users)
+ this.update(&mut cx, |this, _| this.insert(users))
} else {
Ok(Vec::new())
}
})
}
+ pub fn insert(&mut self, users: Vec<proto::User>) -> Vec<Arc<User>> {
+ let mut ret = Vec::with_capacity(users.len());
+ for user in users {
+ let user = User::new(user);
+ if let Some(old) = self.users.insert(user.id, user.clone()) {
+ if old.github_login != user.github_login {
+ self.by_github_login.remove(&old.github_login);
+ }
+ }
+ self.by_github_login
+ .insert(user.github_login.clone(), user.id);
+ ret.push(user)
+ }
+ ret
+ }
+
pub fn set_participant_indices(
&mut self,
participant_indices: HashMap<u64, ParticipantIndex>,
@@ -509,8 +509,7 @@ pub type NotificationBatch = Vec<(UserId, proto::Notification)>;
pub struct CreatedChannelMessage {
pub message_id: MessageId,
- pub participant_connection_ids: Vec<ConnectionId>,
- pub channel_members: Vec<UserId>,
+ pub participant_connection_ids: HashSet<ConnectionId>,
pub notifications: NotificationBatch,
}
@@ -440,12 +440,7 @@ impl Database {
channel_id: ChannelId,
user: UserId,
operations: &[proto::Operation],
- ) -> Result<(
- Vec<ConnectionId>,
- Vec<UserId>,
- i32,
- Vec<proto::VectorClockEntry>,
- )> {
+ ) -> Result<(HashSet<ConnectionId>, i32, Vec<proto::VectorClockEntry>)> {
self.transaction(move |tx| async move {
let channel = self.get_channel_internal(channel_id, &tx).await?;
@@ -479,7 +474,6 @@ impl Database {
.filter_map(|op| operation_to_storage(op, &buffer, serialization_version))
.collect::<Vec<_>>();
- let mut channel_members;
let max_version;
if !operations.is_empty() {
@@ -504,12 +498,6 @@ impl Database {
)
.await?;
- channel_members = self.get_channel_participants(&channel, &tx).await?;
- let collaborators = self
- .get_channel_buffer_collaborators_internal(channel_id, &tx)
- .await?;
- channel_members.retain(|member| !collaborators.contains(member));
-
buffer_operation::Entity::insert_many(operations)
.on_conflict(
OnConflict::columns([
@@ -524,11 +512,10 @@ impl Database {
.exec(&*tx)
.await?;
} else {
- channel_members = Vec::new();
max_version = Vec::new();
}
- let mut connections = Vec::new();
+ let mut connections = HashSet::default();
let mut rows = channel_buffer_collaborator::Entity::find()
.filter(
Condition::all()
@@ -538,13 +525,13 @@ impl Database {
.await?;
while let Some(row) = rows.next().await {
let row = row?;
- connections.push(ConnectionId {
+ connections.insert(ConnectionId {
id: row.connection_id as u32,
owner_id: row.connection_server_id.0 as u32,
});
}
- Ok((connections, channel_members, buffer.epoch, max_version))
+ Ok((connections, buffer.epoch, max_version))
})
.await
}
@@ -3,7 +3,7 @@ use rpc::{
proto::{channel_member::Kind, ChannelBufferVersion, VectorClockEntry},
ErrorCode, ErrorCodeExt,
};
-use sea_orm::TryGetableMany;
+use sea_orm::{DbBackend, TryGetableMany};
impl Database {
#[cfg(test)]
@@ -700,77 +700,73 @@ impl Database {
pub async fn get_channel_participant_details(
&self,
channel_id: ChannelId,
+ filter: &str,
+ limit: u64,
user_id: UserId,
- ) -> Result<Vec<proto::ChannelMember>> {
- let (role, members) = self
+ ) -> Result<(Vec<proto::ChannelMember>, Vec<proto::User>)> {
+ let members = self
.transaction(move |tx| async move {
let channel = self.get_channel_internal(channel_id, &tx).await?;
- let role = self
- .check_user_is_channel_participant(&channel, user_id, &tx)
+ self.check_user_is_channel_participant(&channel, user_id, &tx)
.await?;
- Ok((
- role,
- self.get_channel_participant_details_internal(&channel, &tx)
- .await?,
- ))
+ let mut query = channel_member::Entity::find()
+ .find_also_related(user::Entity)
+ .filter(channel_member::Column::ChannelId.eq(channel.root_id()));
+
+ if cfg!(any(test, sqlite)) && self.pool.get_database_backend() == DbBackend::Sqlite {
+ query = query.filter(Expr::cust_with_values(
+ "UPPER(github_login) LIKE ?",
+ [Self::fuzzy_like_string(&filter.to_uppercase())],
+ ))
+ } else {
+ query = query.filter(Expr::cust_with_values(
+ "github_login ILIKE $1",
+ [Self::fuzzy_like_string(filter)],
+ ))
+ }
+ let members = query.order_by(
+ Expr::cust(
+ "not role = 'admin', not role = 'member', not role = 'guest', not accepted, github_login",
+ ),
+ sea_orm::Order::Asc,
+ )
+ .limit(limit)
+ .all(&*tx)
+ .await?;
+
+ Ok(members)
})
.await?;
- if role == ChannelRole::Admin {
- Ok(members
- .into_iter()
- .map(|channel_member| proto::ChannelMember {
- role: channel_member.role.into(),
- user_id: channel_member.user_id.to_proto(),
- kind: if channel_member.accepted {
+ let mut users: Vec<proto::User> = Vec::with_capacity(members.len());
+
+ let members = members
+ .into_iter()
+ .map(|(member, user)| {
+ if let Some(user) = user {
+ users.push(proto::User {
+ id: user.id.to_proto(),
+ avatar_url: format!(
+ "https://github.com/{}.png?size=128",
+ user.github_login
+ ),
+ github_login: user.github_login,
+ })
+ }
+ proto::ChannelMember {
+ role: member.role.into(),
+ user_id: member.user_id.to_proto(),
+ kind: if member.accepted {
Kind::Member
} else {
Kind::Invitee
}
.into(),
- })
- .collect())
- } else {
- return Ok(members
- .into_iter()
- .filter_map(|member| {
- if !member.accepted {
- return None;
- }
- Some(proto::ChannelMember {
- role: member.role.into(),
- user_id: member.user_id.to_proto(),
- kind: Kind::Member.into(),
- })
- })
- .collect());
- }
- }
-
- async fn get_channel_participant_details_internal(
- &self,
- channel: &channel::Model,
- tx: &DatabaseTransaction,
- ) -> Result<Vec<channel_member::Model>> {
- Ok(channel_member::Entity::find()
- .filter(channel_member::Column::ChannelId.eq(channel.root_id()))
- .all(tx)
- .await?)
- }
+ }
+ })
+ .collect();
- /// Returns the participants in the given channel.
- pub async fn get_channel_participants(
- &self,
- channel: &channel::Model,
- tx: &DatabaseTransaction,
- ) -> Result<Vec<UserId>> {
- let participants = self
- .get_channel_participant_details_internal(channel, tx)
- .await?;
- Ok(participants
- .into_iter()
- .map(|member| member.user_id)
- .collect())
+ Ok((members, users))
}
/// Returns whether the given user is an admin in the specified channel.
@@ -251,7 +251,7 @@ impl Database {
.await?;
let mut is_participant = false;
- let mut participant_connection_ids = Vec::new();
+ let mut participant_connection_ids = HashSet::default();
let mut participant_user_ids = Vec::new();
while let Some(row) = rows.next().await {
let row = row?;
@@ -259,7 +259,7 @@ impl Database {
is_participant = true;
}
participant_user_ids.push(row.user_id);
- participant_connection_ids.push(row.connection());
+ participant_connection_ids.insert(row.connection());
}
drop(rows);
@@ -336,13 +336,9 @@ impl Database {
}
}
- let mut channel_members = self.get_channel_participants(&channel, &tx).await?;
- channel_members.retain(|member| !participant_user_ids.contains(member));
-
Ok(CreatedChannelMessage {
message_id,
participant_connection_ids,
- channel_members,
notifications,
})
})
@@ -1,7 +1,7 @@
use crate::{
db::{
tests::{channel_tree, new_test_connection, new_test_user},
- Channel, ChannelId, ChannelRole, Database, NewUserParams, RoomId,
+ Channel, ChannelId, ChannelRole, Database, NewUserParams, RoomId, UserId,
},
test_both_dbs,
};
@@ -40,15 +40,15 @@ async fn test_channels(db: &Arc<Database>) {
.await
.unwrap();
- let mut members = db
- .transaction(|tx| async move {
- let channel = db.get_channel_internal(replace_id, &tx).await?;
- db.get_channel_participants(&channel, &tx).await
- })
+ let (members, _) = db
+ .get_channel_participant_details(replace_id, "", 10, a_id)
.await
.unwrap();
- members.sort();
- assert_eq!(members, &[a_id, b_id]);
+ let ids = members
+ .into_iter()
+ .map(|m| UserId::from_proto(m.user_id))
+ .collect::<Vec<_>>();
+ assert_eq!(ids, &[a_id, b_id]);
let rust_id = db.create_root_channel("rust", a_id).await.unwrap();
let cargo_id = db.create_sub_channel("cargo", rust_id, a_id).await.unwrap();
@@ -195,8 +195,8 @@ async fn test_channel_invites(db: &Arc<Database>) {
assert_eq!(user_3_invites, &[channel_1_1]);
- let mut members = db
- .get_channel_participant_details(channel_1_1, user_1)
+ let (mut members, _) = db
+ .get_channel_participant_details(channel_1_1, "", 100, user_1)
.await
.unwrap();
@@ -231,8 +231,8 @@ async fn test_channel_invites(db: &Arc<Database>) {
.await
.unwrap();
- let members = db
- .get_channel_participant_details(channel_1_3, user_1)
+ let (members, _) = db
+ .get_channel_participant_details(channel_1_3, "", 100, user_1)
.await
.unwrap();
assert_eq!(
@@ -243,16 +243,16 @@ async fn test_channel_invites(db: &Arc<Database>) {
kind: proto::channel_member::Kind::Member.into(),
role: proto::ChannelRole::Admin.into(),
},
- proto::ChannelMember {
- user_id: user_2.to_proto(),
- kind: proto::channel_member::Kind::Member.into(),
- role: proto::ChannelRole::Member.into(),
- },
proto::ChannelMember {
user_id: user_3.to_proto(),
kind: proto::channel_member::Kind::Invitee.into(),
role: proto::ChannelRole::Admin.into(),
},
+ proto::ChannelMember {
+ user_id: user_2.to_proto(),
+ kind: proto::channel_member::Kind::Member.into(),
+ role: proto::ChannelRole::Member.into(),
+ },
]
);
}
@@ -482,8 +482,8 @@ async fn test_user_is_channel_participant(db: &Arc<Database>) {
.await
.unwrap();
- let mut members = db
- .get_channel_participant_details(public_channel_id, admin)
+ let (mut members, _) = db
+ .get_channel_participant_details(public_channel_id, "", 100, admin)
.await
.unwrap();
@@ -557,8 +557,8 @@ async fn test_user_is_channel_participant(db: &Arc<Database>) {
.await
.is_err());
- let mut members = db
- .get_channel_participant_details(public_channel_id, admin)
+ let (mut members, _) = db
+ .get_channel_participant_details(public_channel_id, "", 100, admin)
.await
.unwrap();
@@ -594,8 +594,8 @@ async fn test_user_is_channel_participant(db: &Arc<Database>) {
.unwrap();
// currently people invited to parent channels are not shown here
- let mut members = db
- .get_channel_participant_details(public_channel_id, admin)
+ let (mut members, _) = db
+ .get_channel_participant_details(public_channel_id, "", 100, admin)
.await
.unwrap();
@@ -663,8 +663,8 @@ async fn test_user_is_channel_participant(db: &Arc<Database>) {
.await
.unwrap();
- let mut members = db
- .get_channel_participant_details(public_channel_id, admin)
+ let (mut members, _) = db
+ .get_channel_participant_details(public_channel_id, "", 100, admin)
.await
.unwrap();
@@ -3683,10 +3683,15 @@ async fn get_channel_members(
) -> Result<()> {
let db = session.db().await;
let channel_id = ChannelId::from_proto(request.channel_id);
- let members = db
- .get_channel_participant_details(channel_id, session.user_id())
+ let limit = if request.limit == 0 {
+ u16::MAX as u64
+ } else {
+ request.limit
+ };
+ let (members, users) = db
+ .get_channel_participant_details(channel_id, &request.query, limit, session.user_id())
.await?;
- response.send(proto::GetChannelMembersResponse { members })?;
+ response.send(proto::GetChannelMembersResponse { members, users })?;
Ok(())
}
@@ -3886,13 +3891,13 @@ async fn update_channel_buffer(
let db = session.db().await;
let channel_id = ChannelId::from_proto(request.channel_id);
- let (collaborators, non_collaborators, epoch, version) = db
+ let (collaborators, epoch, version) = db
.update_channel_buffer(channel_id, session.user_id(), &request.operations)
.await?;
channel_buffer_updated(
session.connection_id,
- collaborators,
+ collaborators.clone(),
&proto::UpdateChannelBuffer {
channel_id: channel_id.to_proto(),
operations: request.operations,
@@ -3902,25 +3907,29 @@ async fn update_channel_buffer(
let pool = &*session.connection_pool().await;
- broadcast(
- None,
- non_collaborators
- .iter()
- .flat_map(|user_id| pool.user_connection_ids(*user_id)),
- |peer_id| {
- session.peer.send(
- peer_id,
- proto::UpdateChannels {
- latest_channel_buffer_versions: vec![proto::ChannelBufferVersion {
- channel_id: channel_id.to_proto(),
- epoch: epoch as u64,
- version: version.clone(),
- }],
- ..Default::default()
- },
- )
- },
- );
+ let non_collaborators =
+ pool.channel_connection_ids(channel_id)
+ .filter_map(|(connection_id, _)| {
+ if collaborators.contains(&connection_id) {
+ None
+ } else {
+ Some(connection_id)
+ }
+ });
+
+ broadcast(None, non_collaborators, |peer_id| {
+ session.peer.send(
+ peer_id,
+ proto::UpdateChannels {
+ latest_channel_buffer_versions: vec![proto::ChannelBufferVersion {
+ channel_id: channel_id.to_proto(),
+ epoch: epoch as u64,
+ version: version.clone(),
+ }],
+ ..Default::default()
+ },
+ )
+ });
Ok(())
}
@@ -4048,7 +4057,6 @@ async fn send_channel_message(
let CreatedChannelMessage {
message_id,
participant_connection_ids,
- channel_members,
notifications,
} = session
.db()
@@ -4079,7 +4087,7 @@ async fn send_channel_message(
};
broadcast(
Some(session.connection_id),
- participant_connection_ids,
+ participant_connection_ids.clone(),
|connection| {
session.peer.send(
connection,
@@ -4095,24 +4103,27 @@ async fn send_channel_message(
})?;
let pool = &*session.connection_pool().await;
- broadcast(
- None,
- channel_members
- .iter()
- .flat_map(|user_id| pool.user_connection_ids(*user_id)),
- |peer_id| {
- session.peer.send(
- peer_id,
- proto::UpdateChannels {
- latest_channel_message_ids: vec![proto::ChannelMessageId {
- channel_id: channel_id.to_proto(),
- message_id: message_id.to_proto(),
- }],
- ..Default::default()
- },
- )
- },
- );
+ let non_participants =
+ pool.channel_connection_ids(channel_id)
+ .filter_map(|(connection_id, _)| {
+ if participant_connection_ids.contains(&connection_id) {
+ None
+ } else {
+ Some(connection_id)
+ }
+ });
+ broadcast(None, non_participants, |peer_id| {
+ session.peer.send(
+ peer_id,
+ proto::UpdateChannels {
+ latest_channel_message_ids: vec![proto::ChannelMessageId {
+ channel_id: channel_id.to_proto(),
+ message_id: message_id.to_proto(),
+ }],
+ ..Default::default()
+ },
+ )
+ });
send_notifications(pool, &session.peer, notifications);
Ok(())
@@ -99,7 +99,7 @@ async fn test_core_channels(
.channel_store()
.update(cx_a, |store, cx| {
assert!(!store.has_pending_channel_invite(channel_a_id, client_b.user_id().unwrap()));
- store.get_channel_member_details(channel_a_id, cx)
+ store.fuzzy_search_members(channel_a_id, "".to_string(), 10, cx)
})
.await
.unwrap();
@@ -1569,11 +1569,28 @@ impl CollabPanel {
*pending_name = Some(channel_name.clone());
- self.channel_store
- .update(cx, |channel_store, cx| {
- channel_store.create_channel(&channel_name, *location, cx)
+ let create = self.channel_store.update(cx, |channel_store, cx| {
+ channel_store.create_channel(&channel_name, *location, cx)
+ });
+ if location.is_none() {
+ cx.spawn(|this, mut cx| async move {
+ let channel_id = create.await?;
+ this.update(&mut cx, |this, cx| {
+ this.show_channel_modal(
+ channel_id,
+ channel_modal::Mode::InviteMembers,
+ cx,
+ )
+ })
})
- .detach();
+ .detach_and_prompt_err(
+ "Failed to create channel",
+ cx,
+ |_, _| None,
+ );
+ } else {
+ create.detach_and_prompt_err("Failed to create channel", cx, |_, _| None);
+ }
cx.notify();
}
ChannelEditingState::Rename {
@@ -1859,12 +1876,8 @@ impl CollabPanel {
let workspace = self.workspace.clone();
let user_store = self.user_store.clone();
let channel_store = self.channel_store.clone();
- let members = self.channel_store.update(cx, |channel_store, cx| {
- channel_store.get_channel_member_details(channel_id, cx)
- });
cx.spawn(|_, mut cx| async move {
- let members = members.await?;
workspace.update(&mut cx, |workspace, cx| {
workspace.toggle_modal(cx, |cx| {
ChannelModal::new(
@@ -1872,7 +1885,6 @@ impl CollabPanel {
channel_store.clone(),
channel_id,
mode,
- members,
cx,
)
});
@@ -37,7 +37,6 @@ impl ChannelModal {
channel_store: Model<ChannelStore>,
channel_id: ChannelId,
mode: Mode,
- members: Vec<ChannelMembership>,
cx: &mut ViewContext<Self>,
) -> Self {
cx.observe(&channel_store, |_, _, cx| cx.notify()).detach();
@@ -54,7 +53,8 @@ impl ChannelModal {
channel_id,
match_candidates: Vec::new(),
context_menu: None,
- members,
+ members: Vec::new(),
+ has_all_members: false,
mode,
},
cx,
@@ -78,37 +78,15 @@ impl ChannelModal {
}
fn set_mode(&mut self, mode: Mode, cx: &mut ViewContext<Self>) {
- let channel_store = self.channel_store.clone();
- let channel_id = self.channel_id;
- cx.spawn(|this, mut cx| async move {
- if mode == Mode::ManageMembers {
- let mut members = channel_store
- .update(&mut cx, |channel_store, cx| {
- channel_store.get_channel_member_details(channel_id, cx)
- })?
- .await?;
-
- members.sort_by(|a, b| a.sort_key().cmp(&b.sort_key()));
-
- this.update(&mut cx, |this, cx| {
- this.picker
- .update(cx, |picker, _| picker.delegate.members = members);
- })?;
- }
-
- this.update(&mut cx, |this, cx| {
- this.picker.update(cx, |picker, cx| {
- let delegate = &mut picker.delegate;
- delegate.mode = mode;
- delegate.selected_index = 0;
- picker.set_query("", cx);
- picker.update_matches(picker.query(cx), cx);
- cx.notify()
- });
- cx.notify()
- })
- })
- .detach();
+ self.picker.update(cx, |picker, cx| {
+ let delegate = &mut picker.delegate;
+ delegate.mode = mode;
+ delegate.selected_index = 0;
+ picker.set_query("", cx);
+ picker.update_matches(picker.query(cx), cx);
+ cx.notify()
+ });
+ cx.notify()
}
fn set_channel_visibility(&mut self, selection: &Selection, cx: &mut ViewContext<Self>) {
@@ -260,6 +238,7 @@ pub struct ChannelModalDelegate {
mode: Mode,
match_candidates: Vec<StringMatchCandidate>,
members: Vec<ChannelMembership>,
+ has_all_members: bool,
context_menu: Option<(View<ContextMenu>, Subscription)>,
}
@@ -288,37 +267,59 @@ impl PickerDelegate for ChannelModalDelegate {
fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()> {
match self.mode {
Mode::ManageMembers => {
- self.match_candidates.clear();
- self.match_candidates
- .extend(self.members.iter().enumerate().map(|(id, member)| {
- StringMatchCandidate {
- id,
- string: member.user.github_login.clone(),
- char_bag: member.user.github_login.chars().collect(),
+ if self.has_all_members {
+ self.match_candidates.clear();
+ self.match_candidates
+ .extend(self.members.iter().enumerate().map(|(id, member)| {
+ StringMatchCandidate {
+ id,
+ string: member.user.github_login.clone(),
+ char_bag: member.user.github_login.chars().collect(),
+ }
+ }));
+
+ let matches = cx.background_executor().block(match_strings(
+ &self.match_candidates,
+ &query,
+ true,
+ usize::MAX,
+ &Default::default(),
+ cx.background_executor().clone(),
+ ));
+
+ cx.spawn(|picker, mut cx| async move {
+ picker
+ .update(&mut cx, |picker, cx| {
+ let delegate = &mut picker.delegate;
+ delegate.matching_member_indices.clear();
+ delegate
+ .matching_member_indices
+ .extend(matches.into_iter().map(|m| m.candidate_id));
+ cx.notify();
+ })
+ .ok();
+ })
+ } else {
+ let search_members = self.channel_store.update(cx, |store, cx| {
+ store.fuzzy_search_members(self.channel_id, query.clone(), 100, cx)
+ });
+ cx.spawn(|picker, mut cx| async move {
+ async {
+ let members = search_members.await?;
+ picker.update(&mut cx, |picker, cx| {
+ picker.delegate.has_all_members =
+ query == "" && members.len() < 100;
+ picker.delegate.matching_member_indices =
+ (0..members.len()).collect();
+ picker.delegate.members = members;
+ cx.notify();
+ })?;
+ anyhow::Ok(())
}
- }));
-
- let matches = cx.background_executor().block(match_strings(
- &self.match_candidates,
- &query,
- true,
- usize::MAX,
- &Default::default(),
- cx.background_executor().clone(),
- ));
-
- cx.spawn(|picker, mut cx| async move {
- picker
- .update(&mut cx, |picker, cx| {
- let delegate = &mut picker.delegate;
- delegate.matching_member_indices.clear();
- delegate
- .matching_member_indices
- .extend(matches.into_iter().map(|m| m.candidate_id));
- cx.notify();
- })
- .ok();
- })
+ .log_err()
+ .await;
+ })
+ }
}
Mode::InviteMembers => {
let search_users = self
@@ -1268,10 +1268,13 @@ message DeleteChannel {
message GetChannelMembers {
uint64 channel_id = 1;
+ string query = 2;
+ uint64 limit = 3;
}
message GetChannelMembersResponse {
repeated ChannelMember members = 1;
+ repeated User users = 2;
}
message ChannelMember {