diff --git a/crates/title_bar/src/collab.rs b/crates/title_bar/src/collab.rs index d740dd90984cd3cbbfd058f7a00a07bb7326f0cd..474d0d287e47dcc6aad0b0f5c57fce382ebf2ca9 100644 --- a/crates/title_bar/src/collab.rs +++ b/crates/title_bar/src/collab.rs @@ -383,10 +383,29 @@ impl TitleBar { ConnectionQuality::Poor => (IconName::SignalMedium, Some(Color::Warning), "Poor"), ConnectionQuality::Lost => (IconName::SignalLow, Some(Color::Error), "Lost"), }; + let quality_label: SharedString = quality_label.into(); + + children.push( + h_flex() + .gap_1() + .child( + IconButton::new("leave-call", IconName::Exit) + .style(ButtonStyle::Subtle) + .tooltip(Tooltip::text("Leave Call")) + .icon_size(IconSize::Small) + .on_click(move |_, _window, cx| { + ActiveCall::global(cx) + .update(cx, |call, cx| call.hang_up(cx)) + .detach_and_log_err(cx); + }), + ) + .child(Divider::vertical().color(DividerColor::Border)) + .into_any_element(), + ); + children.push( IconButton::new("call-quality", signal_icon) - .style(ButtonStyle::Subtle) .icon_size(IconSize::Small) .when_some(signal_color, |button, color| button.icon_color(color)) .tooltip(move |_window, cx| { @@ -413,23 +432,6 @@ impl TitleBar { }) .into_any_element(), ); - children.push( - h_flex() - .gap_1() - .child( - IconButton::new("leave-call", IconName::Exit) - .style(ButtonStyle::Subtle) - .tooltip(Tooltip::text("Leave Call")) - .icon_size(IconSize::Small) - .on_click(move |_, _window, cx| { - ActiveCall::global(cx) - .update(cx, |call, cx| call.hang_up(cx)) - .detach_and_log_err(cx); - }), - ) - .child(Divider::vertical().color(DividerColor::Border)) - .into_any_element(), - ); if is_local && can_share_projects && !is_connecting_to_project { let is_sharing_disabled = channel.is_some_and(|channel| match channel.visibility { diff --git a/crates/title_bar/src/title_bar.rs b/crates/title_bar/src/title_bar.rs index b013a593c86553c40124910a1ff1610ca1f6f00d..a393f71ec73e6b7acb2cdac38f50302410714749 100644 --- a/crates/title_bar/src/title_bar.rs +++ b/crates/title_bar/src/title_bar.rs @@ -27,9 +27,9 @@ use client::{Client, UserStore, zed_urls}; use cloud_api_types::Plan; use gpui::{ - Action, AnyElement, App, Context, Corner, Element, Empty, Entity, Focusable, - InteractiveElement, IntoElement, MouseButton, ParentElement, Render, - StatefulInteractiveElement, Styled, Subscription, WeakEntity, Window, actions, div, + Action, AnyElement, App, Context, Corner, Element, Entity, Focusable, InteractiveElement, + IntoElement, MouseButton, ParentElement, Render, StatefulInteractiveElement, Styled, + Subscription, WeakEntity, Window, actions, div, }; use onboarding_banner::OnboardingBanner; use project::{Project, git_store::GitStoreEvent, trusted_worktrees::TrustedWorktrees}; @@ -260,7 +260,6 @@ impl Render for TitleBar { user.is_none() && TitleBarSettings::get_global(cx).show_sign_in, |this| this.child(self.render_sign_in_button(cx)), ) - .child(self.render_organization_menu_button(cx)) .when(TitleBarSettings::get_global(cx).show_user_menu, |this| { this.child(self.render_user_menu_button(cx)) }) @@ -1132,110 +1131,70 @@ impl TitleBar { }) } - pub fn render_organization_menu_button(&mut self, cx: &mut Context) -> AnyElement { - let Some(organization) = self.user_store.read(cx).current_organization() else { - return Empty.into_any_element(); - }; - - PopoverMenu::new("organization-menu") - .anchor(Corner::TopRight) - .menu({ - let user_store = self.user_store.clone(); - move |window, cx| { - ContextMenu::build(window, cx, |mut menu, _window, cx| { - menu = menu.header("Organizations").separator(); - - let current_organization = user_store.read(cx).current_organization(); - - for organization in user_store.read(cx).organizations() { - let organization = organization.clone(); - let plan = user_store.read(cx).plan_for_organization(&organization.id); - - let is_current = - current_organization - .as_ref() - .is_some_and(|current_organization| { - current_organization.id == organization.id - }); - - menu = menu.custom_entry( - { - let organization = organization.clone(); - move |_window, _cx| { - h_flex() - .w_full() - .gap_1() - .child( - div() - .flex_none() - .when(!is_current, |parent| parent.invisible()) - .child(Icon::new(IconName::Check)), - ) - .child( - h_flex() - .w_full() - .gap_3() - .justify_between() - .child(Label::new(&organization.name)) - .child(PlanChip::new( - plan.unwrap_or(Plan::ZedFree), - )), - ) - .into_any_element() - } - }, - { - let user_store = user_store.clone(); - let organization = organization.clone(); - move |_window, cx| { - user_store.update(cx, |user_store, cx| { - user_store - .set_current_organization(organization.clone(), cx); - }); - } - }, - ); - } - - menu - }) - .into() - } - }) - .trigger_with_tooltip( - Button::new("organization-menu", &organization.name) - .selected_style(ButtonStyle::Tinted(TintColor::Accent)) - .label_size(LabelSize::Small), - Tooltip::text("Toggle Organization Menu"), - ) - .anchor(gpui::Corner::TopRight) - .into_any_element() - } - pub fn render_user_menu_button(&mut self, cx: &mut Context) -> impl Element { - let show_update_badge = self.update_version.read(cx).show_update_in_menu_bar(); + let show_update_button = self.update_version.read(cx).show_update_in_menu_bar(); - let user_store = self.user_store.read(cx); - let user = user_store.current_user(); + let user_store = self.user_store.clone(); + let user_store_read = user_store.read(cx); + let user = user_store_read.current_user(); let user_avatar = user.as_ref().map(|u| u.avatar_uri.clone()); let user_login = user.as_ref().map(|u| u.github_login.clone()); let is_signed_in = user.is_some(); - let has_subscription_period = user_store.subscription_period().is_some(); - let plan = user_store.plan().filter(|_| { + let has_subscription_period = user_store_read.subscription_period().is_some(); + let plan = user_store_read.plan().filter(|_| { // Since the user might be on the legacy free plan we filter based on whether we have a subscription period. has_subscription_period }); + let has_organization = user_store_read.current_organization().is_some(); + + let current_organization = user_store_read.current_organization(); + let organizations: Vec<_> = user_store_read + .organizations() + .iter() + .map(|org| { + let plan = user_store_read.plan_for_organization(&org.id); + (org.clone(), plan) + }) + .collect(); + + let show_user_picture = TitleBarSettings::get_global(cx).show_user_picture; + + let trigger = if is_signed_in && show_user_picture { + let avatar = user_avatar.map(|avatar| Avatar::new(avatar)).map(|avatar| { + if show_update_button { + avatar.indicator( + div() + .absolute() + .bottom_0() + .right_0() + .child(Indicator::dot().color(Color::Accent)), + ) + } else { + avatar + } + }); + + ButtonLike::new("user-menu").children(avatar) + } else { + ButtonLike::new("user-menu") + .child(Icon::new(IconName::ChevronDown).size(IconSize::Small)) + }; + PopoverMenu::new("user-menu") - .anchor(Corner::TopRight) + .trigger(trigger) .menu(move |window, cx| { - ContextMenu::build(window, cx, |menu, _, _cx| { - let user_login = user_login.clone(); + let user_login = user_login.clone(); + let current_organization = current_organization.clone(); + let organizations = organizations.clone(); + let user_store = user_store.clone(); + ContextMenu::build(window, cx, |menu, _, _cx| { menu.when(is_signed_in, |this| { + let user_login = user_login.clone(); this.custom_entry( move |_window, _cx| { let user_login = user_login.clone().unwrap_or_default(); @@ -1253,7 +1212,7 @@ impl TitleBar { ) .separator() }) - .when(show_update_badge, |this| { + .when(show_update_button, |this| { this.custom_entry( move |_window, _cx| { h_flex() @@ -1274,6 +1233,58 @@ impl TitleBar { ) .separator() }) + .when(has_organization, |this| { + let mut this = this.header("Organization"); + + for (organization, plan) in &organizations { + let organization = organization.clone(); + let plan = *plan; + + let is_current = + current_organization + .as_ref() + .is_some_and(|current_organization| { + current_organization.id == organization.id + }); + + this = this.custom_entry( + { + let organization = organization.clone(); + move |_window, _cx| { + h_flex() + .w_full() + .gap_4() + .justify_between() + .child( + h_flex() + .gap_1() + .child(Label::new(&organization.name)) + .when(is_current, |this| { + this.child( + Icon::new(IconName::Check) + .color(Color::Accent), + ) + }), + ) + .child(PlanChip::new(plan.unwrap_or(Plan::ZedFree))) + .into_any_element() + } + }, + { + let user_store = user_store.clone(); + let organization = organization.clone(); + move |_window, cx| { + user_store.update(cx, |user_store, cx| { + user_store + .set_current_organization(organization.clone(), cx); + }); + } + }, + ); + } + + this.separator() + }) .action("Settings", zed_actions::OpenSettings.boxed_clone()) .action("Keymap", Box::new(zed_actions::OpenKeymap)) .action( @@ -1295,37 +1306,6 @@ impl TitleBar { }) .into() }) - .map(|this| { - if is_signed_in && TitleBarSettings::get_global(cx).show_user_picture { - let avatar = - user_avatar - .clone() - .map(|avatar| Avatar::new(avatar)) - .map(|avatar| { - if show_update_badge { - avatar.indicator( - div() - .absolute() - .bottom_0() - .right_0() - .child(Indicator::dot().color(Color::Accent)), - ) - } else { - avatar - } - }); - this.trigger_with_tooltip( - ButtonLike::new("user-menu").children(avatar), - Tooltip::text("Toggle User Menu"), - ) - } else { - this.trigger_with_tooltip( - IconButton::new("user-menu", IconName::ChevronDown) - .icon_size(IconSize::Small), - Tooltip::text("Toggle User Menu"), - ) - } - }) - .anchor(gpui::Corner::TopRight) + .anchor(Corner::TopRight) } }