From 18e7305b6d8fa1377b09e79fceb5bfae9f885d07 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Mon, 2 Oct 2023 23:20:06 -0600 Subject: [PATCH 1/5] Change channel join behavior - Clicking on a channel name now joins the channel if you are not in it - (or opens the notes if you are already there). - When joining a channel, previously shared projects are opened automatically. - If there are no previously shared projects, the notes are opened. --- crates/call/src/call.rs | 6 ++-- crates/call/src/room.rs | 27 +++++++++++++++ crates/collab_ui/src/collab_panel.rs | 51 ++++++++++++++++++++++++---- 3 files changed, 74 insertions(+), 10 deletions(-) diff --git a/crates/call/src/call.rs b/crates/call/src/call.rs index ca0d06beb6..d86ed1be37 100644 --- a/crates/call/src/call.rs +++ b/crates/call/src/call.rs @@ -291,10 +291,10 @@ impl ActiveCall { &mut self, channel_id: u64, cx: &mut ModelContext, - ) -> Task> { + ) -> Task>> { if let Some(room) = self.room().cloned() { if room.read(cx).channel_id() == Some(channel_id) { - return Task::ready(Ok(())); + return Task::ready(Ok(room)); } else { room.update(cx, |room, cx| room.clear_state(cx)); } @@ -309,7 +309,7 @@ impl ActiveCall { this.update(&mut cx, |this, cx| { this.report_call_event("join channel", cx) }); - Ok(()) + Ok(room) }) } diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index 130a7a64f0..f24a8e9a9c 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -594,6 +594,33 @@ impl Room { .map_or(&[], |v| v.as_slice()) } + /// projects_to_join returns a list of shared projects sorted such + /// that the most 'active' projects appear last. + pub fn projects_to_join(&self) -> Vec<(u64, u64)> { + let mut projects = HashMap::default(); + let mut hosts = HashMap::default(); + for participant in self.remote_participants.values() { + match participant.location { + ParticipantLocation::SharedProject { project_id } => { + *projects.entry(project_id).or_insert(0) += 1; + } + ParticipantLocation::External | ParticipantLocation::UnsharedProject => {} + } + for project in &participant.projects { + *projects.entry(project.id).or_insert(0) += 1; + hosts.insert(project.id, participant.user.id); + } + } + + let mut pairs: Vec<(u64, usize)> = projects.into_iter().collect(); + pairs.sort_by_key(|(_, count)| 0 - *count as i32); + + pairs + .into_iter() + .map(|(project_id, _)| (project_id, hosts[&project_id])) + .collect() + } + async fn handle_room_updated( this: ModelHandle, envelope: TypedEnvelope, diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs index 22ab573974..eeae37bbe5 100644 --- a/crates/collab_ui/src/collab_panel.rs +++ b/crates/collab_ui/src/collab_panel.rs @@ -8,7 +8,7 @@ use crate::{ panel_settings, CollaborationPanelSettings, }; use anyhow::Result; -use call::ActiveCall; +use call::{participant, ActiveCall}; use channel::{Channel, ChannelData, ChannelEvent, ChannelId, ChannelPath, ChannelStore}; use channel_modal::ChannelModal; use client::{proto::PeerId, Client, Contact, User, UserStore}; @@ -1929,7 +1929,7 @@ impl CollabPanel { })) .into_any() } else if row_hovered { - Svg::new("icons/speaker-loud.svg") + Svg::new("icons/file.svg") .with_color(theme.channel_hash.color) .constrained() .with_width(theme.channel_hash.width) @@ -1939,7 +1939,11 @@ impl CollabPanel { } }) .on_click(MouseButton::Left, move |_, this, cx| { - this.join_channel_call(channel_id, cx); + if !is_active { + this.join_channel_call(channel_id, cx); + } else { + this.open_channel_notes(&OpenChannelNotes { channel_id }, cx) + } }), ) .align_children_center() @@ -1968,6 +1972,12 @@ impl CollabPanel { }) .on_click(MouseButton::Left, move |_, this, cx| { if this.drag_target_channel.take().is_none() { + if is_active { + this.open_channel_notes(&OpenChannelNotes { channel_id }, cx) + } else { + this.join_channel_call(channel_id, cx) + } + this.join_channel_chat(channel_id, cx); } }) @@ -2991,10 +3001,37 @@ impl CollabPanel { .detach_and_log_err(cx); } - fn join_channel_call(&self, channel: u64, cx: &mut ViewContext) { - ActiveCall::global(cx) - .update(cx, |call, cx| call.join_channel(channel, cx)) - .detach_and_log_err(cx); + fn join_channel_call(&self, channel_id: u64, cx: &mut ViewContext) { + let join = ActiveCall::global(cx).update(cx, |call, cx| call.join_channel(channel_id, cx)); + let workspace = self.workspace.clone(); + + cx.spawn(|_, mut cx| async move { + let room = join.await?; + + let tasks = room.update(&mut cx, |room, cx| { + let Some(workspace) = workspace.upgrade(cx) else { + return vec![]; + }; + let projects = room.projects_to_join(); + + if projects.is_empty() { + ChannelView::open(channel_id, workspace, cx).detach(); + return vec![]; + } + room.projects_to_join() + .into_iter() + .map(|(project_id, user_id)| { + let app_state = workspace.read(cx).app_state().clone(); + workspace::join_remote_project(project_id, user_id, app_state, cx) + }) + .collect() + }); + for task in tasks { + task.await?; + } + Ok::<(), anyhow::Error>(()) + }) + .detach_and_log_err(cx); } fn join_channel_chat(&mut self, channel_id: u64, cx: &mut ViewContext) { From 66dfa47c668555688515c926b0cb1c0fd35aa3a8 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Tue, 3 Oct 2023 11:36:01 -0600 Subject: [PATCH 2/5] Update collab ui to join channels again --- crates/collab_ui/src/collab_panel.rs | 126 +++++++++++++++++++++------ 1 file changed, 100 insertions(+), 26 deletions(-) diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs index eeae37bbe5..08c5dd70ad 100644 --- a/crates/collab_ui/src/collab_panel.rs +++ b/crates/collab_ui/src/collab_panel.rs @@ -8,7 +8,7 @@ use crate::{ panel_settings, CollaborationPanelSettings, }; use anyhow::Result; -use call::{participant, ActiveCall}; +use call::ActiveCall; use channel::{Channel, ChannelData, ChannelEvent, ChannelId, ChannelPath, ChannelStore}; use channel_modal::ChannelModal; use client::{proto::PeerId, Client, Contact, User, UserStore}; @@ -95,6 +95,11 @@ pub struct JoinChannelCall { pub channel_id: u64, } +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct JoinChannelChat { + pub channel_id: u64, +} + #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] struct StartMoveChannelFor { channel_id: ChannelId, @@ -151,6 +156,7 @@ impl_actions!( ToggleCollapse, OpenChannelNotes, JoinChannelCall, + JoinChannelChat, LinkChannel, StartMoveChannelFor, StartLinkChannelFor, @@ -198,6 +204,7 @@ pub fn init(cx: &mut AppContext) { cx.add_action(CollabPanel::collapse_selected_channel); cx.add_action(CollabPanel::expand_selected_channel); cx.add_action(CollabPanel::open_channel_notes); + cx.add_action(CollabPanel::join_channel_chat); cx.add_action( |panel: &mut CollabPanel, action: &ToggleSelectedIx, cx: &mut ViewContext| { @@ -471,6 +478,12 @@ impl CollabPanel { .iter() .position(|entry| !matches!(entry, ListEntry::Header(_))); } + } else if let editor::Event::Blurred = event { + let query = this.filter_editor.read(cx).text(cx); + if query.is_empty() { + this.selection.take(); + this.update_entries(true, cx); + } } }) .detach(); @@ -555,7 +568,7 @@ impl CollabPanel { &*channel, *depth, path.to_owned(), - &theme.collab_panel, + &theme, is_selected, ix, cx, @@ -1827,12 +1840,13 @@ impl CollabPanel { channel: &Channel, depth: usize, path: ChannelPath, - theme: &theme::CollabPanel, + theme: &theme::Theme, is_selected: bool, ix: usize, cx: &mut ViewContext, ) -> AnyElement { let channel_id = channel.id; + let collab_theme = &theme.collab_panel; let has_children = self.channel_store.read(cx).has_children(channel_id); let other_selected = self.selected_channel().map(|channel| channel.0.id) == Some(channel.id); @@ -1851,6 +1865,8 @@ impl CollabPanel { const FACEPILE_LIMIT: usize = 3; enum ChannelCall {} + enum IconTooltip {} + enum ChannelTooltip {} let mut is_dragged_over = false; if cx @@ -1886,18 +1902,29 @@ impl CollabPanel { Flex::::row() .with_child( Svg::new("icons/hash.svg") - .with_color(theme.channel_hash.color) + .with_color(collab_theme.channel_hash.color) .constrained() - .with_width(theme.channel_hash.width) + .with_width(collab_theme.channel_hash.width) .aligned() .left(), ) .with_child( - Label::new(channel.name.clone(), theme.channel_name.text.clone()) + Label::new(channel.name.clone(), collab_theme.channel_name.text.clone()) .contained() - .with_style(theme.channel_name.container) + .with_style(collab_theme.channel_name.container) .aligned() .left() + .with_tooltip::( + channel_id as usize, + if is_active { + "Open channel notes" + } else { + "Join channel" + }, + None, + theme.tooltip.clone(), + cx, + ) .flex(1., true), ) .with_child( @@ -1907,14 +1934,14 @@ impl CollabPanel { if !participants.is_empty() { let extra_count = participants.len().saturating_sub(FACEPILE_LIMIT); - FacePile::new(theme.face_overlap) + FacePile::new(collab_theme.face_overlap) .with_children( participants .iter() .filter_map(|user| { Some( Image::from_data(user.avatar.clone()?) - .with_style(theme.channel_avatar), + .with_style(collab_theme.channel_avatar), ) }) .take(FACEPILE_LIMIT), @@ -1922,28 +1949,48 @@ impl CollabPanel { .with_children((extra_count > 0).then(|| { Label::new( format!("+{}", extra_count), - theme.extra_participant_label.text.clone(), + collab_theme.extra_participant_label.text.clone(), ) .contained() - .with_style(theme.extra_participant_label.container) + .with_style(collab_theme.extra_participant_label.container) })) + .with_tooltip::( + channel_id as usize, + if is_active { + "Open Channel Notes" + } else { + "Join channel" + }, + None, + theme.tooltip.clone(), + cx, + ) .into_any() } else if row_hovered { Svg::new("icons/file.svg") - .with_color(theme.channel_hash.color) + .with_color(collab_theme.channel_hash.color) .constrained() - .with_width(theme.channel_hash.width) + .with_width(collab_theme.channel_hash.width) + .with_tooltip::( + channel_id as usize, + "Open channel notes", + None, + theme.tooltip.clone(), + cx, + ) .into_any() } else { Empty::new().into_any() } }) .on_click(MouseButton::Left, move |_, this, cx| { - if !is_active { - this.join_channel_call(channel_id, cx); + let participants = + this.channel_store.read(cx).channel_participants(channel_id); + if is_active || participants.is_empty() { + this.open_channel_notes(&OpenChannelNotes { channel_id }, cx); } else { - this.open_channel_notes(&OpenChannelNotes { channel_id }, cx) - } + this.join_channel_call(channel_id, cx); + }; }), ) .align_children_center() @@ -1955,19 +2002,19 @@ impl CollabPanel { }), ) .with_id(ix) - .with_style(theme.disclosure.clone()) + .with_style(collab_theme.disclosure.clone()) .element() .constrained() - .with_height(theme.row_height) + .with_height(collab_theme.row_height) .contained() .with_style(select_state( - theme + collab_theme .channel_row .in_state(is_selected || is_active || is_dragged_over), )) .with_padding_left( - theme.channel_row.default_style().padding.left - + theme.channel_indent * depth as f32, + collab_theme.channel_row.default_style().padding.left + + collab_theme.channel_indent * depth as f32, ) }) .on_click(MouseButton::Left, move |_, this, cx| { @@ -1977,8 +2024,6 @@ impl CollabPanel { } else { this.join_channel_call(channel_id, cx) } - - this.join_channel_chat(channel_id, cx); } }) .on_click(MouseButton::Right, { @@ -2402,6 +2447,13 @@ impl CollabPanel { }, )); + items.push(ContextMenuItem::action( + "Open Chat", + JoinChannelChat { + channel_id: path.channel_id(), + }, + )); + if self.channel_store.read(cx).is_user_admin(path.channel_id()) { let parent_id = path.parent_id(); @@ -2598,7 +2650,28 @@ impl CollabPanel { } } ListEntry::Channel { channel, .. } => { - self.join_channel_chat(channel.id, cx); + let is_active = iife!({ + let call_channel = ActiveCall::global(cx) + .read(cx) + .room()? + .read(cx) + .channel_id()?; + + dbg!(call_channel, channel.id); + Some(call_channel == channel.id) + }) + .unwrap_or(false); + dbg!(is_active); + if is_active { + self.open_channel_notes( + &OpenChannelNotes { + channel_id: channel.id, + }, + cx, + ) + } else { + self.join_channel_call(channel.id, cx) + } } ListEntry::ContactPlaceholder => self.toggle_contact_finder(cx), _ => {} @@ -3034,7 +3107,8 @@ impl CollabPanel { .detach_and_log_err(cx); } - fn join_channel_chat(&mut self, channel_id: u64, cx: &mut ViewContext) { + fn join_channel_chat(&mut self, action: &JoinChannelChat, cx: &mut ViewContext) { + let channel_id = action.channel_id; if let Some(workspace) = self.workspace.upgrade(cx) { cx.app_context().defer(move |cx| { workspace.update(cx, |workspace, cx| { From d8bfe77a3b4e307393e928156fdac1d8c2c83254 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Tue, 3 Oct 2023 12:00:02 -0600 Subject: [PATCH 3/5] Scroll so that collab panel is in good state for calls --- crates/collab_ui/src/collab_panel.rs | 84 ++++++++++++++++------------ 1 file changed, 48 insertions(+), 36 deletions(-) diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs index 08c5dd70ad..a7080ad051 100644 --- a/crates/collab_ui/src/collab_panel.rs +++ b/crates/collab_ui/src/collab_panel.rs @@ -781,9 +781,16 @@ impl CollabPanel { 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); @@ -1151,44 +1158,49 @@ impl CollabPanel { } let old_scroll_top = self.list_state.logical_scroll_top(); + self.list_state.reset(self.entries.len()); - // 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 { + 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: 0., + offset_in_item: old_scroll_top.offset_in_item, }) - }) - .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., + .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)); + self.list_state + .scroll_to(new_scroll_top.unwrap_or(old_scroll_top)); + } } cx.notify(); @@ -1989,7 +2001,7 @@ impl CollabPanel { if is_active || participants.is_empty() { this.open_channel_notes(&OpenChannelNotes { channel_id }, cx); } else { - this.join_channel_call(channel_id, cx); + this.join_channel(channel_id, cx); }; }), ) @@ -2022,7 +2034,7 @@ impl CollabPanel { if is_active { this.open_channel_notes(&OpenChannelNotes { channel_id }, cx) } else { - this.join_channel_call(channel_id, cx) + this.join_channel(channel_id, cx) } } }) @@ -2670,7 +2682,7 @@ impl CollabPanel { cx, ) } else { - self.join_channel_call(channel.id, cx) + self.join_channel(channel.id, cx) } } ListEntry::ContactPlaceholder => self.toggle_contact_finder(cx), @@ -3074,7 +3086,7 @@ impl CollabPanel { .detach_and_log_err(cx); } - fn join_channel_call(&self, channel_id: u64, cx: &mut ViewContext) { + fn join_channel(&self, channel_id: u64, cx: &mut ViewContext) { let join = ActiveCall::global(cx).update(cx, |call, cx| call.join_channel(channel_id, cx)); let workspace = self.workspace.clone(); From d696b394c45daa5212ece6e4914ba684aa925441 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Tue, 3 Oct 2023 12:54:39 -0600 Subject: [PATCH 4/5] Tooltips for contacts --- crates/collab_ui/src/collab_panel.rs | 114 ++++++++++++++++++--------- 1 file changed, 77 insertions(+), 37 deletions(-) diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs index a7080ad051..ce18a7b92d 100644 --- a/crates/collab_ui/src/collab_panel.rs +++ b/crates/collab_ui/src/collab_panel.rs @@ -621,7 +621,7 @@ impl CollabPanel { contact, *calling, &this.project, - &theme.collab_panel, + &theme, is_selected, cx, ), @@ -1658,15 +1658,19 @@ impl CollabPanel { contact: &Contact, calling: bool, project: &ModelHandle, - theme: &theme::CollabPanel, + theme: &theme::Theme, is_selected: bool, cx: &mut ViewContext, ) -> AnyElement { + enum ContactTooltip {}; + + let collab_theme = &theme.collab_panel; let online = contact.online; let busy = contact.busy || calling; let user_id = contact.user.id; let github_login = contact.user.github_login.clone(); let initial_project = project.clone(); + let mut event_handler = MouseEventHandler::new::(contact.user.id as usize, cx, |state, cx| { Flex::row() @@ -1677,9 +1681,9 @@ impl CollabPanel { .collapsed() .contained() .with_style(if busy { - theme.contact_status_busy + collab_theme.contact_status_busy } else { - theme.contact_status_free + collab_theme.contact_status_free }) .aligned(), ) @@ -1689,7 +1693,7 @@ impl CollabPanel { Stack::new() .with_child( Image::from_data(avatar) - .with_style(theme.contact_avatar) + .with_style(collab_theme.contact_avatar) .aligned() .left(), ) @@ -1698,58 +1702,94 @@ impl CollabPanel { .with_child( Label::new( contact.user.github_login.clone(), - theme.contact_username.text.clone(), + collab_theme.contact_username.text.clone(), ) .contained() - .with_style(theme.contact_username.container) + .with_style(collab_theme.contact_username.container) .aligned() .left() .flex(1., true), ) - .with_child( - MouseEventHandler::new::( - contact.user.id as usize, - cx, - |mouse_state, _| { - let button_style = theme.contact_button.style_for(mouse_state); - render_icon_button(button_style, "icons/x.svg") - .aligned() - .flex_float() - }, + .with_children(if state.hovered() { + Some( + MouseEventHandler::new::( + contact.user.id as usize, + cx, + |mouse_state, _| { + let button_style = + collab_theme.contact_button.style_for(mouse_state); + render_icon_button(button_style, "icons/x.svg") + .aligned() + .flex_float() + }, + ) + .with_padding(Padding::uniform(2.)) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, move |_, this, cx| { + this.remove_contact(user_id, &github_login, cx); + }) + .flex_float(), ) - .with_padding(Padding::uniform(2.)) - .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, move |_, this, cx| { - this.remove_contact(user_id, &github_login, cx); - }) - .flex_float(), - ) + } else { + None + }) .with_children(if calling { Some( - Label::new("Calling", theme.calling_indicator.text.clone()) + Label::new("Calling", collab_theme.calling_indicator.text.clone()) .contained() - .with_style(theme.calling_indicator.container) + .with_style(collab_theme.calling_indicator.container) .aligned(), ) } else { None }) .constrained() - .with_height(theme.row_height) + .with_height(collab_theme.row_height) .contained() - .with_style(*theme.contact_row.in_state(is_selected).style_for(state)) - }) - .on_click(MouseButton::Left, move |_, this, cx| { - if online && !busy { - this.call(user_id, Some(initial_project.clone()), cx); - } + .with_style( + *collab_theme + .contact_row + .in_state(is_selected) + .style_for(state), + ) }); - if online { - event_handler = event_handler.with_cursor_style(CursorStyle::PointingHand); - } + if online && !busy { + let room = ActiveCall::global(cx).read(cx).room(); + let label = if room.is_some() { + format!("Invite {} to join call", contact.user.github_login) + } else { + format!("Call {}", contact.user.github_login) + }; - event_handler.into_any() + event_handler + .on_click(MouseButton::Left, move |_, this, cx| { + this.call(user_id, Some(initial_project.clone()), cx); + }) + .with_cursor_style(CursorStyle::PointingHand) + .with_tooltip::( + contact.user.id as usize, + label, + None, + theme.tooltip.clone(), + cx, + ) + .into_any() + } else { + event_handler + .with_tooltip::( + contact.user.id as usize, + format!( + "{} is {}", + contact.user.github_login, + if busy { "on a call" } else { "offline" } + ), + None, + theme.tooltip.clone(), + cx, + ) + .into_any() + } } fn render_contact_placeholder( From 044fb9e2f5ac99afdd2cd6b10f7d5adea3ef258c Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Tue, 3 Oct 2023 13:41:24 -0600 Subject: [PATCH 5/5] Confirm on switching channels --- crates/collab_ui/src/collab_panel.rs | 29 ++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs index ce18a7b92d..ab6261c568 100644 --- a/crates/collab_ui/src/collab_panel.rs +++ b/crates/collab_ui/src/collab_panel.rs @@ -1662,7 +1662,7 @@ impl CollabPanel { is_selected: bool, cx: &mut ViewContext, ) -> AnyElement { - enum ContactTooltip {}; + enum ContactTooltip {} let collab_theme = &theme.collab_panel; let online = contact.online; @@ -1671,7 +1671,7 @@ impl CollabPanel { let github_login = contact.user.github_login.clone(); let initial_project = project.clone(); - let mut event_handler = + let event_handler = MouseEventHandler::new::(contact.user.id as usize, cx, |state, cx| { Flex::row() .with_children(contact.user.avatar.clone().map(|avatar| { @@ -3127,11 +3127,28 @@ impl CollabPanel { } fn join_channel(&self, channel_id: u64, cx: &mut ViewContext) { - let join = ActiveCall::global(cx).update(cx, |call, cx| call.join_channel(channel_id, cx)); let workspace = self.workspace.clone(); - + let window = cx.window(); + let active_call = ActiveCall::global(cx); cx.spawn(|_, mut cx| async move { - let room = join.await?; + if active_call.read_with(&mut cx, |active_call, _| active_call.room().is_some()) { + let answer = window.prompt( + PromptLevel::Warning, + "Do you want to leave the current call?", + &["Yes, Join Channel", "Cancel"], + &mut cx, + ); + + if let Some(mut answer) = answer { + if answer.next().await == Some(1) { + return anyhow::Ok(()); + } + } + } + + let room = active_call + .update(&mut cx, |call, cx| call.join_channel(channel_id, cx)) + .await?; let tasks = room.update(&mut cx, |room, cx| { let Some(workspace) = workspace.upgrade(cx) else { @@ -3154,7 +3171,7 @@ impl CollabPanel { for task in tasks { task.await?; } - Ok::<(), anyhow::Error>(()) + anyhow::Ok(()) }) .detach_and_log_err(cx); }