Detailed changes
@@ -1973,7 +1973,7 @@ impl AgentPanel {
return None;
}
- if self.user_store.read(cx).current_user_account_too_young() {
+ if self.user_store.read(cx).account_too_young() {
Some(self.render_young_account_upsell(cx).into_any_element())
} else {
Some(self.render_trial_upsell(cx).into_any_element())
@@ -109,6 +109,7 @@ pub struct UserStore {
edit_predictions_usage_limit: Option<proto::UsageLimit>,
is_usage_based_billing_enabled: Option<bool>,
account_too_young: Option<bool>,
+ has_overdue_invoices: Option<bool>,
current_user: watch::Receiver<Option<Arc<User>>>,
accepted_tos_at: Option<Option<DateTime<Utc>>>,
contacts: Vec<Arc<Contact>>,
@@ -176,6 +177,7 @@ impl UserStore {
edit_predictions_usage_limit: None,
is_usage_based_billing_enabled: None,
account_too_young: None,
+ has_overdue_invoices: None,
accepted_tos_at: None,
contacts: Default::default(),
incoming_contact_requests: Default::default(),
@@ -350,6 +352,7 @@ impl UserStore {
.and_then(|trial_started_at| DateTime::from_timestamp(trial_started_at as i64, 0));
this.is_usage_based_billing_enabled = message.payload.is_usage_based_billing_enabled;
this.account_too_young = message.payload.account_too_young;
+ this.has_overdue_invoices = message.payload.has_overdue_invoices;
if let Some(usage) = message.payload.usage {
this.model_request_usage_amount = Some(usage.model_requests_usage_amount);
@@ -755,11 +758,16 @@ impl UserStore {
self.current_user.clone()
}
- /// Check if the current user's account is too new to use the service
- pub fn current_user_account_too_young(&self) -> bool {
+ /// Returns whether the user's account is too new to use the service.
+ pub fn account_too_young(&self) -> bool {
self.account_too_young.unwrap_or(false)
}
+ /// Returns whether the current user has overdue invoices and usage should be blocked.
+ pub fn has_overdue_invoices(&self) -> bool {
+ self.has_overdue_invoices.unwrap_or(false)
+ }
+
pub fn current_user_has_accepted_terms(&self) -> Option<bool> {
self.accepted_tos_at
.map(|accepted_tos_at| accepted_tos_at.is_some())
@@ -1,5 +1,5 @@
use crate::db::billing_subscription::SubscriptionKind;
-use crate::db::{billing_subscription, user};
+use crate::db::{billing_customer, billing_subscription, user};
use crate::llm::AGENT_EXTENDED_TRIAL_FEATURE_FLAG;
use crate::{Config, db::billing_preference};
use anyhow::{Context as _, Result};
@@ -32,6 +32,8 @@ pub struct LlmTokenClaims {
pub enable_model_request_overages: bool,
pub model_request_overages_spend_limit_in_cents: u32,
pub can_use_web_search_tool: bool,
+ #[serde(default)]
+ pub has_overdue_invoices: bool,
}
const LLM_TOKEN_LIFETIME: Duration = Duration::from_secs(60 * 60);
@@ -40,6 +42,7 @@ impl LlmTokenClaims {
pub fn create(
user: &user::Model,
is_staff: bool,
+ billing_customer: billing_customer::Model,
billing_preferences: Option<billing_preference::Model>,
feature_flags: &Vec<String>,
subscription: billing_subscription::Model,
@@ -99,6 +102,7 @@ impl LlmTokenClaims {
.map_or(0, |preferences| {
preferences.model_request_overages_spend_limit_in_cents as u32
}),
+ has_overdue_invoices: billing_customer.has_overdue_invoices,
};
Ok(jsonwebtoken::encode(
@@ -2748,6 +2748,7 @@ async fn make_update_user_plan_message(
Ok(proto::UpdateUserPlan {
plan: plan.into(),
trial_started_at: billing_customer
+ .as_ref()
.and_then(|billing_customer| billing_customer.trial_started_at)
.map(|trial_started_at| trial_started_at.and_utc().timestamp() as u64),
is_usage_based_billing_enabled: if is_staff {
@@ -2762,6 +2763,8 @@ async fn make_update_user_plan_message(
}
}),
account_too_young: Some(account_too_young),
+ has_overdue_invoices: billing_customer
+ .map(|billing_customer| billing_customer.has_overdue_invoices),
usage: usage.map(|usage| {
let plan = match plan {
proto::Plan::Free => zed_llm_client::Plan::ZedFree,
@@ -4077,6 +4080,7 @@ async fn get_llm_api_token(
let token = LlmTokenClaims::create(
&user,
session.is_staff(),
+ billing_customer,
billing_preferences,
&flags,
billing_subscription,
@@ -745,7 +745,7 @@ impl InlineCompletionButton {
})
})
.separator();
- } else if self.user_store.read(cx).current_user_account_too_young() {
+ } else if self.user_store.read(cx).account_too_young() {
menu = menu
.custom_entry(
|_window, _cx| {
@@ -785,6 +785,46 @@ impl InlineCompletionButton {
},
)
.separator();
+ } else if self.user_store.read(cx).has_overdue_invoices() {
+ menu = menu
+ .custom_entry(
+ |_window, _cx| {
+ h_flex()
+ .gap_1()
+ .child(
+ Icon::new(IconName::Warning)
+ .size(IconSize::Small)
+ .color(Color::Warning),
+ )
+ .child(
+ Label::new("You have an outstanding invoice")
+ .size(LabelSize::Small)
+ .color(Color::Warning),
+ )
+ .into_any_element()
+ },
+ |window, cx| {
+ window.dispatch_action(
+ Box::new(OpenZedUrl {
+ url: zed_urls::account_url(cx),
+ }),
+ cx,
+ );
+ },
+ )
+ .entry(
+ "Check your payment status or contact us at billing-support@zed.dev to continue using this feature.",
+ None,
+ |window, cx| {
+ window.dispatch_action(
+ Box::new(OpenZedUrl {
+ url: zed_urls::account_url(cx),
+ }),
+ cx,
+ );
+ },
+ )
+ .separator();
}
self.build_language_settings_menu(menu, window, cx).when(
@@ -28,6 +28,7 @@ message UpdateUserPlan {
optional SubscriptionUsage usage = 4;
optional SubscriptionPeriod subscription_period = 5;
optional bool account_too_young = 6;
+ optional bool has_overdue_invoices = 7;
}
message SubscriptionPeriod {
@@ -1578,8 +1578,9 @@ impl inline_completion::EditPredictionProvider for ZetaInlineCompletionProvider
.zeta
.read(cx)
.user_store
- .read(cx)
- .current_user_account_too_young()
+ .read_with(cx, |user_store, _| {
+ user_store.account_too_young() || user_store.has_overdue_invoices()
+ })
{
return;
}