Cargo.lock 🔗
@@ -11651,6 +11651,7 @@ dependencies = [
"auto_update2",
"backtrace",
"call2",
+ "channel2",
"chrono",
"cli",
"client2",
Conrad Irwin created
Cargo.lock | 1
crates/collab_ui2/src/collab_panel.rs | 1081 ++++++++++++++--------------
crates/zed2/Cargo.toml | 2
crates/zed2/src/main.rs | 4
4 files changed, 546 insertions(+), 542 deletions(-)
@@ -11651,6 +11651,7 @@ dependencies = [
"auto_update2",
"backtrace",
"call2",
+ "channel2",
"chrono",
"cli",
"client2",
@@ -1,3 +1,4 @@
+#![allow(unused)]
// mod channel_modal;
// mod contact_finder;
@@ -155,20 +156,27 @@ actions!(
const COLLABORATION_PANEL_KEY: &'static str = "CollaborationPanel";
-use std::{iter::once, sync::Arc};
+use std::{iter::once, mem, sync::Arc};
-use client::{Client, Contact, UserStore};
+use call::ActiveCall;
+use channel::{Channel, ChannelId, ChannelStore};
+use client::{Client, Contact, User, UserStore};
use db::kvp::KEY_VALUE_STORE;
+use editor::Editor;
+use feature_flags::{ChannelsAlpha, FeatureFlagAppExt};
+use fuzzy::{match_strings, StringMatchCandidate};
use gpui::{
actions, div, serde_json, AppContext, AsyncWindowContext, Div, EventEmitter, FocusHandle,
Focusable, FocusableView, InteractiveElement, IntoElement, Model, ParentElement, Render,
- RenderOnce, Styled, View, ViewContext, VisualContext, WeakView,
+ RenderOnce, SharedString, Styled, Subscription, View, ViewContext, VisualContext, WeakView,
};
use project::Fs;
use serde_derive::{Deserialize, Serialize};
use settings::Settings;
-use ui::{h_stack, v_stack, Avatar, Button, Icon, IconButton, Label, List, ListHeader};
-use util::ResultExt;
+use ui::{
+ h_stack, v_stack, Avatar, Button, Icon, IconButton, Label, List, ListHeader, ListItem, Tooltip,
+};
+use util::{maybe, ResultExt};
use workspace::{
dock::{DockPosition, Panel, PanelEvent},
notifications::NotifyResultExt,
@@ -269,26 +277,26 @@ pub fn init(cx: &mut AppContext) {
// );
}
-// #[derive(Debug)]
-// pub enum ChannelEditingState {
-// Create {
-// location: Option<ChannelId>,
-// pending_name: Option<String>,
-// },
-// Rename {
-// location: ChannelId,
-// pending_name: Option<String>,
-// },
-// }
+#[derive(Debug)]
+pub enum ChannelEditingState {
+ Create {
+ location: Option<ChannelId>,
+ pending_name: Option<String>,
+ },
+ Rename {
+ location: ChannelId,
+ pending_name: Option<String>,
+ },
+}
-// impl ChannelEditingState {
-// fn pending_name(&self) -> Option<&str> {
-// match self {
-// ChannelEditingState::Create { pending_name, .. } => pending_name.as_deref(),
-// ChannelEditingState::Rename { pending_name, .. } => pending_name.as_deref(),
-// }
-// }
-// }
+impl ChannelEditingState {
+ fn pending_name(&self) -> Option<&str> {
+ match self {
+ ChannelEditingState::Create { pending_name, .. } => pending_name.as_deref(),
+ ChannelEditingState::Rename { pending_name, .. } => pending_name.as_deref(),
+ }
+ }
+}
pub struct CollabPanel {
width: Option<f32>,
@@ -297,20 +305,20 @@ pub struct CollabPanel {
// channel_clipboard: Option<ChannelMoveClipboard>,
// pending_serialization: Task<Option<()>>,
// context_menu: ViewHandle<ContextMenu>,
- // filter_editor: ViewHandle<Editor>,
+ filter_editor: View<Editor>,
// channel_name_editor: ViewHandle<Editor>,
- // channel_editing_state: Option<ChannelEditingState>,
- // entries: Vec<ListEntry>,
+ channel_editing_state: Option<ChannelEditingState>,
+ entries: Vec<ListEntry>,
// selection: Option<usize>,
+ channel_store: Model<ChannelStore>,
user_store: Model<UserStore>,
client: Arc<Client>,
- // channel_store: ModelHandle<ChannelStore>,
// project: ModelHandle<Project>,
- // match_candidates: Vec<StringMatchCandidate>,
+ match_candidates: Vec<StringMatchCandidate>,
// list_state: ListState<Self>,
- // subscriptions: Vec<Subscription>,
+ subscriptions: Vec<Subscription>,
collapsed_sections: Vec<Section>,
- // collapsed_channels: Vec<ChannelId>,
+ collapsed_channels: Vec<ChannelId>,
// drag_target_channel: ChannelDragTarget,
workspace: WeakView<Workspace>,
// context_menu_on_selected: bool,
@@ -338,56 +346,56 @@ struct SerializedCollabPanel {
#[derive(Clone, Copy, PartialEq, Eq, Debug, PartialOrd, Ord)]
enum Section {
- // ActiveCall,
- // Channels,
- // ChannelInvites,
- // ContactRequests,
+ ActiveCall,
+ Channels,
+ ChannelInvites,
+ ContactRequests,
Contacts,
- // Online,
+ Online,
Offline,
}
-// #[derive(Clone, Debug)]
-// enum ListEntry {
-// Header(Section),
-// CallParticipant {
-// user: Arc<User>,
-// peer_id: Option<PeerId>,
-// is_pending: bool,
-// },
-// ParticipantProject {
-// project_id: u64,
-// worktree_root_names: Vec<String>,
-// host_user_id: u64,
-// is_last: bool,
-// },
-// ParticipantScreen {
-// peer_id: Option<PeerId>,
-// is_last: bool,
-// },
-// IncomingRequest(Arc<User>),
-// OutgoingRequest(Arc<User>),
-// ChannelInvite(Arc<Channel>),
-// Channel {
-// channel: Arc<Channel>,
-// depth: usize,
-// has_children: bool,
-// },
-// ChannelNotes {
-// channel_id: ChannelId,
-// },
-// ChannelChat {
-// channel_id: ChannelId,
-// },
-// ChannelEditor {
-// depth: usize,
-// },
-// Contact {
-// contact: Arc<Contact>,
-// calling: bool,
-// },
-// ContactPlaceholder,
-// }
+#[derive(Clone, Debug)]
+enum ListEntry {
+ Header(Section),
+ // CallParticipant {
+ // user: Arc<User>,
+ // peer_id: Option<PeerId>,
+ // is_pending: bool,
+ // },
+ // ParticipantProject {
+ // project_id: u64,
+ // worktree_root_names: Vec<String>,
+ // host_user_id: u64,
+ // is_last: bool,
+ // },
+ // ParticipantScreen {
+ // peer_id: Option<PeerId>,
+ // is_last: bool,
+ // },
+ IncomingRequest(Arc<User>),
+ OutgoingRequest(Arc<User>),
+ // ChannelInvite(Arc<Channel>),
+ Channel {
+ channel: Arc<Channel>,
+ depth: usize,
+ has_children: bool,
+ },
+ // ChannelNotes {
+ // channel_id: ChannelId,
+ // },
+ // ChannelChat {
+ // channel_id: ChannelId,
+ // },
+ ChannelEditor {
+ depth: usize,
+ },
+ Contact {
+ contact: Arc<Contact>,
+ calling: bool,
+ },
+ ContactPlaceholder,
+}
// impl Entity for CollabPanel {
// type Event = Event;
@@ -398,16 +406,11 @@ impl CollabPanel {
cx.build_view(|cx| {
// let view_id = cx.view_id();
- // let filter_editor = cx.add_view(|cx| {
- // let mut editor = Editor::single_line(
- // Some(Arc::new(|theme| {
- // theme.collab_panel.user_query_editor.clone()
- // })),
- // cx,
- // );
- // editor.set_placeholder_text("Filter channels, contacts", cx);
- // editor
- // });
+ let filter_editor = cx.build_view(|cx| {
+ let mut editor = Editor::single_line(cx);
+ editor.set_placeholder_text("Filter channels, contacts", cx);
+ editor
+ });
// cx.subscribe(&filter_editor, |this, _, event, cx| {
// if let editor::Event::BufferEdited = event {
@@ -586,7 +589,7 @@ impl CollabPanel {
// }
// });
- let this = Self {
+ let mut this = Self {
width: None,
focus_handle: cx.focus_handle(),
// channel_clipboard: None,
@@ -594,17 +597,17 @@ impl CollabPanel {
// pending_serialization: Task::ready(None),
// context_menu: cx.add_view(|cx| ContextMenu::new(view_id, cx)),
// channel_name_editor,
- // filter_editor,
- // entries: Vec::default(),
- // channel_editing_state: None,
+ filter_editor,
+ entries: Vec::default(),
+ channel_editing_state: None,
// selection: None,
+ channel_store: ChannelStore::global(cx),
user_store: workspace.user_store().clone(),
- // channel_store: ChannelStore::global(cx),
// project: workspace.project().clone(),
- // subscriptions: Vec::default(),
- // match_candidates: Vec::default(),
+ subscriptions: Vec::default(),
+ match_candidates: Vec::default(),
collapsed_sections: vec![Section::Offline],
- // collapsed_channels: Vec::default(),
+ collapsed_channels: Vec::default(),
workspace: workspace.weak_handle(),
client: workspace.app_state().client.clone(),
// context_menu_on_selected: true,
@@ -612,7 +615,7 @@ impl CollabPanel {
// list_state,
};
- // this.update_entries(false, cx);
+ this.update_entries(false, cx);
// // Update the dock position when the setting changes.
// let mut old_dock_position = this.position(cx);
@@ -629,10 +632,10 @@ impl CollabPanel {
// );
// let active_call = ActiveCall::global(cx);
- // this.subscriptions
- // .push(cx.observe(&this.user_store, |this, _, cx| {
- // this.update_entries(true, cx)
- // }));
+ this.subscriptions
+ .push(cx.observe(&this.user_store, |this, _, cx| {
+ this.update_entries(true, cx)
+ }));
// this.subscriptions
// .push(cx.observe(&this.channel_store, |this, _, cx| {
// this.update_entries(true, cx)
@@ -721,449 +724,449 @@ impl CollabPanel {
// );
// }
- // fn update_entries(&mut self, select_same_item: bool, cx: &mut ViewContext<Self>) {
- // let channel_store = self.channel_store.read(cx);
- // let user_store = self.user_store.read(cx);
- // let query = self.filter_editor.read(cx).text(cx);
- // let executor = cx.background().clone();
-
- // let prev_selected_entry = self.selection.and_then(|ix| self.entries.get(ix).cloned());
- // let old_entries = mem::take(&mut self.entries);
- // let mut scroll_to_top = false;
-
- // if let Some(room) = ActiveCall::global(cx).read(cx).room() {
- // self.entries.push(ListEntry::Header(Section::ActiveCall));
- // if !old_entries
- // .iter()
- // .any(|entry| matches!(entry, ListEntry::Header(Section::ActiveCall)))
- // {
- // scroll_to_top = true;
- // }
-
- // if !self.collapsed_sections.contains(&Section::ActiveCall) {
- // let room = room.read(cx);
-
- // if let Some(channel_id) = room.channel_id() {
- // self.entries.push(ListEntry::ChannelNotes { channel_id });
- // self.entries.push(ListEntry::ChannelChat { channel_id })
- // }
-
- // // Populate the active user.
- // if let Some(user) = user_store.current_user() {
- // self.match_candidates.clear();
- // self.match_candidates.push(StringMatchCandidate {
- // id: 0,
- // string: user.github_login.clone(),
- // char_bag: user.github_login.chars().collect(),
- // });
- // let matches = executor.block(match_strings(
- // &self.match_candidates,
- // &query,
- // true,
- // usize::MAX,
- // &Default::default(),
- // executor.clone(),
- // ));
- // if !matches.is_empty() {
- // let user_id = user.id;
- // self.entries.push(ListEntry::CallParticipant {
- // user,
- // peer_id: None,
- // is_pending: false,
- // });
- // let mut projects = room.local_participant().projects.iter().peekable();
- // while let Some(project) = projects.next() {
- // self.entries.push(ListEntry::ParticipantProject {
- // project_id: project.id,
- // worktree_root_names: project.worktree_root_names.clone(),
- // host_user_id: user_id,
- // is_last: projects.peek().is_none() && !room.is_screen_sharing(),
- // });
- // }
- // if room.is_screen_sharing() {
- // self.entries.push(ListEntry::ParticipantScreen {
- // peer_id: None,
- // is_last: true,
- // });
- // }
- // }
- // }
-
- // // Populate remote participants.
- // self.match_candidates.clear();
- // self.match_candidates
- // .extend(room.remote_participants().iter().map(|(_, participant)| {
- // StringMatchCandidate {
- // id: participant.user.id as usize,
- // string: participant.user.github_login.clone(),
- // char_bag: participant.user.github_login.chars().collect(),
- // }
- // }));
- // let matches = executor.block(match_strings(
- // &self.match_candidates,
- // &query,
- // true,
- // usize::MAX,
- // &Default::default(),
- // executor.clone(),
- // ));
- // for mat in matches {
- // let user_id = mat.candidate_id as u64;
- // let participant = &room.remote_participants()[&user_id];
- // self.entries.push(ListEntry::CallParticipant {
- // user: participant.user.clone(),
- // peer_id: Some(participant.peer_id),
- // is_pending: false,
- // });
- // let mut projects = participant.projects.iter().peekable();
- // while let Some(project) = projects.next() {
- // self.entries.push(ListEntry::ParticipantProject {
- // project_id: project.id,
- // worktree_root_names: project.worktree_root_names.clone(),
- // host_user_id: participant.user.id,
- // is_last: projects.peek().is_none()
- // && participant.video_tracks.is_empty(),
- // });
- // }
- // if !participant.video_tracks.is_empty() {
- // self.entries.push(ListEntry::ParticipantScreen {
- // peer_id: Some(participant.peer_id),
- // is_last: true,
- // });
- // }
- // }
-
- // // Populate pending participants.
- // self.match_candidates.clear();
- // self.match_candidates
- // .extend(room.pending_participants().iter().enumerate().map(
- // |(id, participant)| StringMatchCandidate {
- // id,
- // string: participant.github_login.clone(),
- // char_bag: participant.github_login.chars().collect(),
- // },
- // ));
- // let matches = executor.block(match_strings(
- // &self.match_candidates,
- // &query,
- // true,
- // usize::MAX,
- // &Default::default(),
- // executor.clone(),
- // ));
- // self.entries
- // .extend(matches.iter().map(|mat| ListEntry::CallParticipant {
- // user: room.pending_participants()[mat.candidate_id].clone(),
- // peer_id: None,
- // is_pending: true,
- // }));
- // }
- // }
-
- // let mut request_entries = Vec::new();
-
- // if cx.has_flag::<ChannelsAlpha>() {
- // self.entries.push(ListEntry::Header(Section::Channels));
-
- // if channel_store.channel_count() > 0 || self.channel_editing_state.is_some() {
- // self.match_candidates.clear();
- // self.match_candidates
- // .extend(channel_store.ordered_channels().enumerate().map(
- // |(ix, (_, channel))| StringMatchCandidate {
- // id: ix,
- // string: channel.name.clone(),
- // char_bag: channel.name.chars().collect(),
- // },
- // ));
- // let matches = executor.block(match_strings(
- // &self.match_candidates,
- // &query,
- // true,
- // usize::MAX,
- // &Default::default(),
- // executor.clone(),
- // ));
- // if let Some(state) = &self.channel_editing_state {
- // if matches!(state, ChannelEditingState::Create { location: None, .. }) {
- // self.entries.push(ListEntry::ChannelEditor { depth: 0 });
- // }
- // }
- // let mut collapse_depth = None;
- // for mat in matches {
- // let channel = channel_store.channel_at_index(mat.candidate_id).unwrap();
- // let depth = channel.parent_path.len();
-
- // if collapse_depth.is_none() && self.is_channel_collapsed(channel.id) {
- // collapse_depth = Some(depth);
- // } else if let Some(collapsed_depth) = collapse_depth {
- // if depth > collapsed_depth {
- // continue;
- // }
- // if self.is_channel_collapsed(channel.id) {
- // collapse_depth = Some(depth);
- // } else {
- // collapse_depth = None;
- // }
- // }
-
- // let has_children = channel_store
- // .channel_at_index(mat.candidate_id + 1)
- // .map_or(false, |next_channel| {
- // next_channel.parent_path.ends_with(&[channel.id])
- // });
-
- // match &self.channel_editing_state {
- // Some(ChannelEditingState::Create {
- // location: parent_id,
- // ..
- // }) if *parent_id == Some(channel.id) => {
- // self.entries.push(ListEntry::Channel {
- // channel: channel.clone(),
- // depth,
- // has_children: false,
- // });
- // self.entries
- // .push(ListEntry::ChannelEditor { depth: depth + 1 });
- // }
- // Some(ChannelEditingState::Rename {
- // location: parent_id,
- // ..
- // }) if parent_id == &channel.id => {
- // self.entries.push(ListEntry::ChannelEditor { depth });
- // }
- // _ => {
- // self.entries.push(ListEntry::Channel {
- // channel: channel.clone(),
- // depth,
- // has_children,
- // });
- // }
- // }
- // }
- // }
-
- // let channel_invites = channel_store.channel_invitations();
- // if !channel_invites.is_empty() {
- // self.match_candidates.clear();
- // self.match_candidates
- // .extend(channel_invites.iter().enumerate().map(|(ix, channel)| {
- // StringMatchCandidate {
- // id: ix,
- // string: channel.name.clone(),
- // char_bag: channel.name.chars().collect(),
- // }
- // }));
- // let matches = executor.block(match_strings(
- // &self.match_candidates,
- // &query,
- // true,
- // usize::MAX,
- // &Default::default(),
- // executor.clone(),
- // ));
- // request_entries.extend(matches.iter().map(|mat| {
- // ListEntry::ChannelInvite(channel_invites[mat.candidate_id].clone())
- // }));
-
- // if !request_entries.is_empty() {
- // self.entries
- // .push(ListEntry::Header(Section::ChannelInvites));
- // if !self.collapsed_sections.contains(&Section::ChannelInvites) {
- // self.entries.append(&mut request_entries);
- // }
- // }
- // }
- // }
-
- // self.entries.push(ListEntry::Header(Section::Contacts));
-
- // request_entries.clear();
- // let incoming = user_store.incoming_contact_requests();
- // if !incoming.is_empty() {
- // self.match_candidates.clear();
- // self.match_candidates
- // .extend(
- // incoming
- // .iter()
- // .enumerate()
- // .map(|(ix, user)| StringMatchCandidate {
- // id: ix,
- // string: user.github_login.clone(),
- // char_bag: user.github_login.chars().collect(),
- // }),
- // );
- // let matches = executor.block(match_strings(
- // &self.match_candidates,
- // &query,
- // true,
- // usize::MAX,
- // &Default::default(),
- // executor.clone(),
- // ));
- // request_entries.extend(
- // matches
- // .iter()
- // .map(|mat| ListEntry::IncomingRequest(incoming[mat.candidate_id].clone())),
- // );
- // }
-
- // let outgoing = user_store.outgoing_contact_requests();
- // if !outgoing.is_empty() {
- // self.match_candidates.clear();
- // self.match_candidates
- // .extend(
- // outgoing
- // .iter()
- // .enumerate()
- // .map(|(ix, user)| StringMatchCandidate {
- // id: ix,
- // string: user.github_login.clone(),
- // char_bag: user.github_login.chars().collect(),
- // }),
- // );
- // let matches = executor.block(match_strings(
- // &self.match_candidates,
- // &query,
- // true,
- // usize::MAX,
- // &Default::default(),
- // executor.clone(),
- // ));
- // request_entries.extend(
- // matches
- // .iter()
- // .map(|mat| ListEntry::OutgoingRequest(outgoing[mat.candidate_id].clone())),
- // );
- // }
-
- // if !request_entries.is_empty() {
- // self.entries
- // .push(ListEntry::Header(Section::ContactRequests));
- // if !self.collapsed_sections.contains(&Section::ContactRequests) {
- // self.entries.append(&mut request_entries);
- // }
- // }
-
- // let contacts = user_store.contacts();
- // if !contacts.is_empty() {
- // self.match_candidates.clear();
- // self.match_candidates
- // .extend(
- // contacts
- // .iter()
- // .enumerate()
- // .map(|(ix, contact)| StringMatchCandidate {
- // id: ix,
- // string: contact.user.github_login.clone(),
- // char_bag: contact.user.github_login.chars().collect(),
- // }),
- // );
-
- // let matches = executor.block(match_strings(
- // &self.match_candidates,
- // &query,
- // true,
- // usize::MAX,
- // &Default::default(),
- // executor.clone(),
- // ));
-
- // let (online_contacts, offline_contacts) = matches
- // .iter()
- // .partition::<Vec<_>, _>(|mat| contacts[mat.candidate_id].online);
-
- // for (matches, section) in [
- // (online_contacts, Section::Online),
- // (offline_contacts, Section::Offline),
- // ] {
- // if !matches.is_empty() {
- // self.entries.push(ListEntry::Header(section));
- // if !self.collapsed_sections.contains(§ion) {
- // let active_call = &ActiveCall::global(cx).read(cx);
- // for mat in matches {
- // let contact = &contacts[mat.candidate_id];
- // self.entries.push(ListEntry::Contact {
- // contact: contact.clone(),
- // calling: active_call.pending_invites().contains(&contact.user.id),
- // });
- // }
- // }
- // }
- // }
- // }
-
- // if incoming.is_empty() && outgoing.is_empty() && contacts.is_empty() {
- // self.entries.push(ListEntry::ContactPlaceholder);
- // }
-
- // if select_same_item {
- // if let Some(prev_selected_entry) = prev_selected_entry {
- // self.selection.take();
- // for (ix, entry) in self.entries.iter().enumerate() {
- // if *entry == prev_selected_entry {
- // self.selection = Some(ix);
- // break;
- // }
- // }
- // }
- // } else {
- // self.selection = self.selection.and_then(|prev_selection| {
- // if self.entries.is_empty() {
- // None
- // } else {
- // Some(prev_selection.min(self.entries.len() - 1))
- // }
- // });
- // }
-
- // let old_scroll_top = self.list_state.logical_scroll_top();
+ fn update_entries(&mut self, select_same_item: bool, cx: &mut ViewContext<Self>) {
+ let channel_store = self.channel_store.read(cx);
+ let user_store = self.user_store.read(cx);
+ let query = self.filter_editor.read(cx).text(cx);
+ let executor = cx.background_executor().clone();
+
+ // let prev_selected_entry = self.selection.and_then(|ix| self.entries.get(ix).cloned());
+ let _old_entries = mem::take(&mut self.entries);
+ // let mut scroll_to_top = false;
+
+ // if let Some(room) = ActiveCall::global(cx).read(cx).room() {
+ // self.entries.push(ListEntry::Header(Section::ActiveCall));
+ // if !old_entries
+ // .iter()
+ // .any(|entry| matches!(entry, ListEntry::Header(Section::ActiveCall)))
+ // {
+ // scroll_to_top = true;
+ // }
+
+ // if !self.collapsed_sections.contains(&Section::ActiveCall) {
+ // let room = room.read(cx);
+
+ // if let Some(channel_id) = room.channel_id() {
+ // self.entries.push(ListEntry::ChannelNotes { channel_id });
+ // self.entries.push(ListEntry::ChannelChat { channel_id })
+ // }
+
+ // // Populate the active user.
+ // if let Some(user) = user_store.current_user() {
+ // self.match_candidates.clear();
+ // self.match_candidates.push(StringMatchCandidate {
+ // id: 0,
+ // string: user.github_login.clone(),
+ // char_bag: user.github_login.chars().collect(),
+ // });
+ // let matches = executor.block(match_strings(
+ // &self.match_candidates,
+ // &query,
+ // true,
+ // usize::MAX,
+ // &Default::default(),
+ // executor.clone(),
+ // ));
+ // if !matches.is_empty() {
+ // let user_id = user.id;
+ // self.entries.push(ListEntry::CallParticipant {
+ // user,
+ // peer_id: None,
+ // is_pending: false,
+ // });
+ // let mut projects = room.local_participant().projects.iter().peekable();
+ // while let Some(project) = projects.next() {
+ // self.entries.push(ListEntry::ParticipantProject {
+ // project_id: project.id,
+ // worktree_root_names: project.worktree_root_names.clone(),
+ // host_user_id: user_id,
+ // is_last: projects.peek().is_none() && !room.is_screen_sharing(),
+ // });
+ // }
+ // if room.is_screen_sharing() {
+ // self.entries.push(ListEntry::ParticipantScreen {
+ // peer_id: None,
+ // is_last: true,
+ // });
+ // }
+ // }
+ // }
+
+ // // Populate remote participants.
+ // self.match_candidates.clear();
+ // self.match_candidates
+ // .extend(room.remote_participants().iter().map(|(_, participant)| {
+ // StringMatchCandidate {
+ // id: participant.user.id as usize,
+ // string: participant.user.github_login.clone(),
+ // char_bag: participant.user.github_login.chars().collect(),
+ // }
+ // }));
+ // let matches = executor.block(match_strings(
+ // &self.match_candidates,
+ // &query,
+ // true,
+ // usize::MAX,
+ // &Default::default(),
+ // executor.clone(),
+ // ));
+ // for mat in matches {
+ // let user_id = mat.candidate_id as u64;
+ // let participant = &room.remote_participants()[&user_id];
+ // self.entries.push(ListEntry::CallParticipant {
+ // user: participant.user.clone(),
+ // peer_id: Some(participant.peer_id),
+ // is_pending: false,
+ // });
+ // let mut projects = participant.projects.iter().peekable();
+ // while let Some(project) = projects.next() {
+ // self.entries.push(ListEntry::ParticipantProject {
+ // project_id: project.id,
+ // worktree_root_names: project.worktree_root_names.clone(),
+ // host_user_id: participant.user.id,
+ // is_last: projects.peek().is_none()
+ // && participant.video_tracks.is_empty(),
+ // });
+ // }
+ // if !participant.video_tracks.is_empty() {
+ // self.entries.push(ListEntry::ParticipantScreen {
+ // peer_id: Some(participant.peer_id),
+ // is_last: true,
+ // });
+ // }
+ // }
+
+ // // Populate pending participants.
+ // self.match_candidates.clear();
+ // self.match_candidates
+ // .extend(room.pending_participants().iter().enumerate().map(
+ // |(id, participant)| StringMatchCandidate {
+ // id,
+ // string: participant.github_login.clone(),
+ // char_bag: participant.github_login.chars().collect(),
+ // },
+ // ));
+ // let matches = executor.block(match_strings(
+ // &self.match_candidates,
+ // &query,
+ // true,
+ // usize::MAX,
+ // &Default::default(),
+ // executor.clone(),
+ // ));
+ // self.entries
+ // .extend(matches.iter().map(|mat| ListEntry::CallParticipant {
+ // user: room.pending_participants()[mat.candidate_id].clone(),
+ // peer_id: None,
+ // is_pending: true,
+ // }));
+ // }
+ // }
+
+ let mut request_entries = Vec::new();
+
+ if cx.has_flag::<ChannelsAlpha>() {
+ self.entries.push(ListEntry::Header(Section::Channels));
+
+ if channel_store.channel_count() > 0 || self.channel_editing_state.is_some() {
+ self.match_candidates.clear();
+ self.match_candidates
+ .extend(channel_store.ordered_channels().enumerate().map(
+ |(ix, (_, channel))| StringMatchCandidate {
+ id: ix,
+ string: channel.name.clone(),
+ char_bag: channel.name.chars().collect(),
+ },
+ ));
+ let matches = executor.block(match_strings(
+ &self.match_candidates,
+ &query,
+ true,
+ usize::MAX,
+ &Default::default(),
+ executor.clone(),
+ ));
+ if let Some(state) = &self.channel_editing_state {
+ if matches!(state, ChannelEditingState::Create { location: None, .. }) {
+ self.entries.push(ListEntry::ChannelEditor { depth: 0 });
+ }
+ }
+ let mut collapse_depth = None;
+ for mat in matches {
+ let channel = channel_store.channel_at_index(mat.candidate_id).unwrap();
+ let depth = channel.parent_path.len();
+
+ if collapse_depth.is_none() && self.is_channel_collapsed(channel.id) {
+ collapse_depth = Some(depth);
+ } else if let Some(collapsed_depth) = collapse_depth {
+ if depth > collapsed_depth {
+ continue;
+ }
+ if self.is_channel_collapsed(channel.id) {
+ collapse_depth = Some(depth);
+ } else {
+ collapse_depth = None;
+ }
+ }
- // self.list_state.reset(self.entries.len());
+ let has_children = channel_store
+ .channel_at_index(mat.candidate_id + 1)
+ .map_or(false, |next_channel| {
+ next_channel.parent_path.ends_with(&[channel.id])
+ });
+
+ match &self.channel_editing_state {
+ Some(ChannelEditingState::Create {
+ location: parent_id,
+ ..
+ }) if *parent_id == Some(channel.id) => {
+ self.entries.push(ListEntry::Channel {
+ channel: channel.clone(),
+ depth,
+ has_children: false,
+ });
+ self.entries
+ .push(ListEntry::ChannelEditor { depth: depth + 1 });
+ }
+ Some(ChannelEditingState::Rename {
+ location: parent_id,
+ ..
+ }) if parent_id == &channel.id => {
+ self.entries.push(ListEntry::ChannelEditor { depth });
+ }
+ _ => {
+ self.entries.push(ListEntry::Channel {
+ channel: channel.clone(),
+ depth,
+ has_children,
+ });
+ }
+ }
+ }
+ }
- // if scroll_to_top {
- // self.list_state.scroll_to(ListOffset::default());
- // } else {
- // // Attempt to maintain the same scroll position.
- // if let Some(old_top_entry) = old_entries.get(old_scroll_top.item_ix) {
- // let new_scroll_top = self
- // .entries
- // .iter()
- // .position(|entry| entry == old_top_entry)
- // .map(|item_ix| ListOffset {
- // item_ix,
- // offset_in_item: old_scroll_top.offset_in_item,
- // })
- // .or_else(|| {
- // let entry_after_old_top = old_entries.get(old_scroll_top.item_ix + 1)?;
- // let item_ix = self
- // .entries
- // .iter()
- // .position(|entry| entry == entry_after_old_top)?;
- // Some(ListOffset {
- // item_ix,
- // offset_in_item: 0.,
- // })
- // })
- // .or_else(|| {
- // let entry_before_old_top =
- // old_entries.get(old_scroll_top.item_ix.saturating_sub(1))?;
- // let item_ix = self
- // .entries
- // .iter()
- // .position(|entry| entry == entry_before_old_top)?;
- // Some(ListOffset {
- // item_ix,
- // offset_in_item: 0.,
- // })
- // });
+ // let channel_invites = channel_store.channel_invitations();
+ // if !channel_invites.is_empty() {
+ // self.match_candidates.clear();
+ // self.match_candidates
+ // .extend(channel_invites.iter().enumerate().map(|(ix, channel)| {
+ // StringMatchCandidate {
+ // id: ix,
+ // string: channel.name.clone(),
+ // char_bag: channel.name.chars().collect(),
+ // }
+ // }));
+ // let matches = executor.block(match_strings(
+ // &self.match_candidates,
+ // &query,
+ // true,
+ // usize::MAX,
+ // &Default::default(),
+ // executor.clone(),
+ // ));
+ // request_entries.extend(matches.iter().map(|mat| {
+ // ListEntry::ChannelInvite(channel_invites[mat.candidate_id].clone())
+ // }));
- // self.list_state
- // .scroll_to(new_scroll_top.unwrap_or(old_scroll_top));
- // }
- // }
+ // if !request_entries.is_empty() {
+ // self.entries
+ // .push(ListEntry::Header(Section::ChannelInvites));
+ // if !self.collapsed_sections.contains(&Section::ChannelInvites) {
+ // self.entries.append(&mut request_entries);
+ // }
+ // }
+ // }
+ }
+
+ self.entries.push(ListEntry::Header(Section::Contacts));
+
+ request_entries.clear();
+ let incoming = user_store.incoming_contact_requests();
+ if !incoming.is_empty() {
+ self.match_candidates.clear();
+ self.match_candidates
+ .extend(
+ incoming
+ .iter()
+ .enumerate()
+ .map(|(ix, user)| StringMatchCandidate {
+ id: ix,
+ string: user.github_login.clone(),
+ char_bag: user.github_login.chars().collect(),
+ }),
+ );
+ let matches = executor.block(match_strings(
+ &self.match_candidates,
+ &query,
+ true,
+ usize::MAX,
+ &Default::default(),
+ executor.clone(),
+ ));
+ request_entries.extend(
+ matches
+ .iter()
+ .map(|mat| ListEntry::IncomingRequest(incoming[mat.candidate_id].clone())),
+ );
+ }
+
+ let outgoing = user_store.outgoing_contact_requests();
+ if !outgoing.is_empty() {
+ self.match_candidates.clear();
+ self.match_candidates
+ .extend(
+ outgoing
+ .iter()
+ .enumerate()
+ .map(|(ix, user)| StringMatchCandidate {
+ id: ix,
+ string: user.github_login.clone(),
+ char_bag: user.github_login.chars().collect(),
+ }),
+ );
+ let matches = executor.block(match_strings(
+ &self.match_candidates,
+ &query,
+ true,
+ usize::MAX,
+ &Default::default(),
+ executor.clone(),
+ ));
+ request_entries.extend(
+ matches
+ .iter()
+ .map(|mat| ListEntry::OutgoingRequest(outgoing[mat.candidate_id].clone())),
+ );
+ }
+
+ if !request_entries.is_empty() {
+ self.entries
+ .push(ListEntry::Header(Section::ContactRequests));
+ if !self.collapsed_sections.contains(&Section::ContactRequests) {
+ self.entries.append(&mut request_entries);
+ }
+ }
+
+ let contacts = user_store.contacts();
+ if !contacts.is_empty() {
+ self.match_candidates.clear();
+ self.match_candidates
+ .extend(
+ contacts
+ .iter()
+ .enumerate()
+ .map(|(ix, contact)| StringMatchCandidate {
+ id: ix,
+ string: contact.user.github_login.clone(),
+ char_bag: contact.user.github_login.chars().collect(),
+ }),
+ );
+
+ let matches = executor.block(match_strings(
+ &self.match_candidates,
+ &query,
+ true,
+ usize::MAX,
+ &Default::default(),
+ executor.clone(),
+ ));
+
+ let (online_contacts, offline_contacts) = matches
+ .iter()
+ .partition::<Vec<_>, _>(|mat| contacts[mat.candidate_id].online);
+
+ for (matches, section) in [
+ (online_contacts, Section::Online),
+ (offline_contacts, Section::Offline),
+ ] {
+ if !matches.is_empty() {
+ self.entries.push(ListEntry::Header(section));
+ if !self.collapsed_sections.contains(§ion) {
+ let active_call = &ActiveCall::global(cx).read(cx);
+ for mat in matches {
+ let contact = &contacts[mat.candidate_id];
+ self.entries.push(ListEntry::Contact {
+ contact: contact.clone(),
+ calling: active_call.pending_invites().contains(&contact.user.id),
+ });
+ }
+ }
+ }
+ }
+ }
+
+ if incoming.is_empty() && outgoing.is_empty() && contacts.is_empty() {
+ self.entries.push(ListEntry::ContactPlaceholder);
+ }
+
+ // if select_same_item {
+ // if let Some(prev_selected_entry) = prev_selected_entry {
+ // self.selection.take();
+ // for (ix, entry) in self.entries.iter().enumerate() {
+ // if *entry == prev_selected_entry {
+ // self.selection = Some(ix);
+ // break;
+ // }
+ // }
+ // }
+ // } else {
+ // self.selection = self.selection.and_then(|prev_selection| {
+ // if self.entries.is_empty() {
+ // None
+ // } else {
+ // Some(prev_selection.min(self.entries.len() - 1))
+ // }
+ // });
+ // }
+
+ // let old_scroll_top = self.list_state.logical_scroll_top();
+
+ // self.list_state.reset(self.entries.len());
+
+ // if scroll_to_top {
+ // self.list_state.scroll_to(ListOffset::default());
+ // } else {
+ // // Attempt to maintain the same scroll position.
+ // if let Some(old_top_entry) = old_entries.get(old_scroll_top.item_ix) {
+ // let new_scroll_top = self
+ // .entries
+ // .iter()
+ // .position(|entry| entry == old_top_entry)
+ // .map(|item_ix| ListOffset {
+ // item_ix,
+ // offset_in_item: old_scroll_top.offset_in_item,
+ // })
+ // .or_else(|| {
+ // let entry_after_old_top = old_entries.get(old_scroll_top.item_ix + 1)?;
+ // let item_ix = self
+ // .entries
+ // .iter()
+ // .position(|entry| entry == entry_after_old_top)?;
+ // Some(ListOffset {
+ // item_ix,
+ // offset_in_item: 0.,
+ // })
+ // })
+ // .or_else(|| {
+ // let entry_before_old_top =
+ // old_entries.get(old_scroll_top.item_ix.saturating_sub(1))?;
+ // let item_ix = self
+ // .entries
+ // .iter()
+ // .position(|entry| entry == entry_before_old_top)?;
+ // Some(ListOffset {
+ // item_ix,
+ // offset_in_item: 0.,
+ // })
+ // });
+
+ // self.list_state
+ // .scroll_to(new_scroll_top.unwrap_or(old_scroll_top));
+ // }
+ // }
- // cx.notify();
- // }
+ cx.notify();
+ }
// fn render_call_participant(
// user: &User,
@@ -21,7 +21,7 @@ audio = { package = "audio2", path = "../audio2" }
auto_update = { package = "auto_update2", path = "../auto_update2" }
# breadcrumbs = { path = "../breadcrumbs" }
call = { package = "call2", path = "../call2" }
-# channel = { path = "../channel" }
+channel = { package = "channel2", path = "../channel2" }
cli = { path = "../cli" }
collab_ui = { package = "collab_ui2", path = "../collab_ui2" }
collections = { path = "../collections" }
@@ -189,7 +189,7 @@ fn main() {
let app_state = Arc::new(AppState {
languages,
client: client.clone(),
- user_store,
+ user_store: user_store.clone(),
fs,
build_window_options,
call_factory: call::Call::new,
@@ -210,7 +210,7 @@ fn main() {
// outline::init(cx);
// project_symbols::init(cx);
project_panel::init(Assets, cx);
- // channel::init(&client, user_store.clone(), cx);
+ channel::init(&client, user_store.clone(), cx);
// diagnostics::init(cx);
search::init(cx);
// semantic_index::init(fs.clone(), http.clone(), languages.clone(), cx);