diff --git a/crates/client/src/user.rs b/crates/client/src/user.rs index 885095017edf66a9e7a1edb7590e8ebc37e16fc9..d27bf3387a7c8406885f078eef82be694dfa5dfa 100644 --- a/crates/client/src/user.rs +++ b/crates/client/src/user.rs @@ -2,7 +2,9 @@ use super::{Client, Status, TypedEnvelope, proto}; use anyhow::{Context as _, Result}; use chrono::{DateTime, Utc}; use cloud_api_client::websocket_protocol::MessageToClient; -use cloud_api_client::{GetAuthenticatedUserResponse, Organization, Plan, PlanInfo}; +use cloud_api_client::{ + GetAuthenticatedUserResponse, Organization, OrganizationId, Plan, PlanInfo, +}; use cloud_llm_client::{ EDIT_PREDICTIONS_USAGE_AMOUNT_HEADER_NAME, EDIT_PREDICTIONS_USAGE_LIMIT_HEADER_NAME, UsageLimit, }; @@ -111,6 +113,7 @@ pub struct UserStore { current_user: watch::Receiver>>, current_organization: Option>, organizations: Vec>, + plans_by_organization: HashMap, contacts: Vec>, incoming_contact_requests: Vec>, outgoing_contact_requests: Vec>, @@ -185,6 +188,7 @@ impl UserStore { current_user: current_user_rx, current_organization: None, organizations: Vec::new(), + plans_by_organization: HashMap::default(), plan_info: None, edit_prediction_usage: None, contacts: Default::default(), @@ -698,6 +702,10 @@ impl UserStore { &self.organizations } + pub fn plan_for_organization(&self, organization_id: &OrganizationId) -> Option { + self.plans_by_organization.get(organization_id).copied() + } + pub fn plan(&self) -> Option { #[cfg(debug_assertions)] if let Ok(plan) = std::env::var("ZED_SIMULATE_PLAN").as_ref() { @@ -713,6 +721,12 @@ impl UserStore { }; } + if let Some(organization) = &self.current_organization + && let Some(plan) = self.plan_for_organization(&organization.id) + { + return Some(plan); + } + self.plan_info.as_ref().map(|info| info.plan()) } diff --git a/crates/cloud_api_types/src/cloud_api_types.rs b/crates/cloud_api_types/src/cloud_api_types.rs index 4b62405c8abd942f3ee1765319d1c1867ad43065..5f86dce21eea6f76a426fa1bca735be87a513ee2 100644 --- a/crates/cloud_api_types/src/cloud_api_types.rs +++ b/crates/cloud_api_types/src/cloud_api_types.rs @@ -35,8 +35,8 @@ pub struct AuthenticatedUser { pub accepted_tos_at: Option, } -#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] -pub struct OrganizationId(Arc); +#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)] +pub struct OrganizationId(pub Arc); #[derive(Debug, PartialEq, Serialize, Deserialize)] pub struct Organization { diff --git a/crates/title_bar/src/title_bar.rs b/crates/title_bar/src/title_bar.rs index d8b7cb54d4bb94bb1e1d10e61a9297e9c28958a8..b52bde3fdb0b0230c271cc99210fdd13e7eac7cc 100644 --- a/crates/title_bar/src/title_bar.rs +++ b/crates/title_bar/src/title_bar.rs @@ -969,8 +969,18 @@ impl TitleBar { 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( { @@ -978,8 +988,23 @@ impl TitleBar { move |_window, _cx| { h_flex() .w_full() - .justify_between() - .child(Label::new(&organization.name)) + .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() } },