inline_completion_button: Replace UserStore with CloudUserStore (#35456)

This PR replaces usages of the `UserStore` in the inline completion
button with the `CloudUserStore`.

Release Notes:

- N/A
This commit is contained in:
Marshall Bowers 2025-07-31 23:25:23 -04:00 committed by GitHub
parent 8be3f48f37
commit f7f90593ac
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 24 additions and 27 deletions

View file

@ -114,7 +114,6 @@ pub struct UserStore {
trial_started_at: Option<DateTime<Utc>>, trial_started_at: Option<DateTime<Utc>>,
is_usage_based_billing_enabled: Option<bool>, is_usage_based_billing_enabled: Option<bool>,
account_too_young: Option<bool>, account_too_young: Option<bool>,
has_overdue_invoices: Option<bool>,
current_user: watch::Receiver<Option<Arc<User>>>, current_user: watch::Receiver<Option<Arc<User>>>,
accepted_tos_at: Option<Option<DateTime<Utc>>>, accepted_tos_at: Option<Option<DateTime<Utc>>>,
contacts: Vec<Arc<Contact>>, contacts: Vec<Arc<Contact>>,
@ -190,7 +189,6 @@ impl UserStore {
trial_started_at: None, trial_started_at: None,
is_usage_based_billing_enabled: None, is_usage_based_billing_enabled: None,
account_too_young: None, account_too_young: None,
has_overdue_invoices: None,
accepted_tos_at: None, accepted_tos_at: None,
contacts: Default::default(), contacts: Default::default(),
incoming_contact_requests: Default::default(), incoming_contact_requests: Default::default(),
@ -358,7 +356,6 @@ impl UserStore {
.and_then(|trial_started_at| DateTime::from_timestamp(trial_started_at as i64, 0)); .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.is_usage_based_billing_enabled = message.payload.is_usage_based_billing_enabled;
this.account_too_young = message.payload.account_too_young; this.account_too_young = message.payload.account_too_young;
this.has_overdue_invoices = message.payload.has_overdue_invoices;
cx.emit(Event::PlanUpdated); cx.emit(Event::PlanUpdated);
cx.notify(); cx.notify();
@ -755,11 +752,6 @@ impl UserStore {
self.account_too_young.unwrap_or(false) 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> { pub fn current_user_has_accepted_terms(&self) -> Option<bool> {
self.accepted_tos_at self.accepted_tos_at
.map(|accepted_tos_at| accepted_tos_at.is_some()) .map(|accepted_tos_at| accepted_tos_at.is_some())

View file

@ -1,5 +1,5 @@
use anyhow::Result; use anyhow::Result;
use client::{DisableAiSettings, UserStore, zed_urls}; use client::{CloudUserStore, DisableAiSettings, zed_urls};
use cloud_llm_client::UsageLimit; use cloud_llm_client::UsageLimit;
use copilot::{Copilot, Status}; use copilot::{Copilot, Status};
use editor::{ use editor::{
@ -59,7 +59,7 @@ pub struct InlineCompletionButton {
file: Option<Arc<dyn File>>, file: Option<Arc<dyn File>>,
edit_prediction_provider: Option<Arc<dyn inline_completion::InlineCompletionProviderHandle>>, edit_prediction_provider: Option<Arc<dyn inline_completion::InlineCompletionProviderHandle>>,
fs: Arc<dyn Fs>, fs: Arc<dyn Fs>,
user_store: Entity<UserStore>, cloud_user_store: Entity<CloudUserStore>,
popover_menu_handle: PopoverMenuHandle<ContextMenu>, popover_menu_handle: PopoverMenuHandle<ContextMenu>,
} }
@ -245,13 +245,16 @@ impl Render for InlineCompletionButton {
IconName::ZedPredictDisabled IconName::ZedPredictDisabled
}; };
if zeta::should_show_upsell_modal(&self.user_store, cx) { if zeta::should_show_upsell_modal(&self.cloud_user_store, cx) {
let tooltip_meta = let tooltip_meta = if self.cloud_user_store.read(cx).is_authenticated() {
match self.user_store.read(cx).current_user_has_accepted_terms() { if self.cloud_user_store.read(cx).has_accepted_tos() {
Some(true) => "Choose a Plan", "Choose a Plan"
Some(false) => "Accept the Terms of Service", } else {
None => "Sign In", "Accept the Terms of Service"
}; }
} else {
"Sign In"
};
return div().child( return div().child(
IconButton::new("zed-predict-pending-button", zeta_icon) IconButton::new("zed-predict-pending-button", zeta_icon)
@ -368,7 +371,7 @@ impl Render for InlineCompletionButton {
impl InlineCompletionButton { impl InlineCompletionButton {
pub fn new( pub fn new(
fs: Arc<dyn Fs>, fs: Arc<dyn Fs>,
user_store: Entity<UserStore>, cloud_user_store: Entity<CloudUserStore>,
popover_menu_handle: PopoverMenuHandle<ContextMenu>, popover_menu_handle: PopoverMenuHandle<ContextMenu>,
cx: &mut Context<Self>, cx: &mut Context<Self>,
) -> Self { ) -> Self {
@ -389,7 +392,7 @@ impl InlineCompletionButton {
edit_prediction_provider: None, edit_prediction_provider: None,
popover_menu_handle, popover_menu_handle,
fs, fs,
user_store, cloud_user_store,
} }
} }
@ -760,7 +763,7 @@ impl InlineCompletionButton {
}) })
}) })
.separator(); .separator();
} else if self.user_store.read(cx).account_too_young() { } else if self.cloud_user_store.read(cx).account_too_young() {
menu = menu menu = menu
.custom_entry( .custom_entry(
|_window, _cx| { |_window, _cx| {
@ -775,7 +778,7 @@ impl InlineCompletionButton {
cx.open_url(&zed_urls::account_url(cx)) cx.open_url(&zed_urls::account_url(cx))
}) })
.separator(); .separator();
} else if self.user_store.read(cx).has_overdue_invoices() { } else if self.cloud_user_store.read(cx).has_overdue_invoices() {
menu = menu menu = menu
.custom_entry( .custom_entry(
|_window, _cx| { |_window, _cx| {

View file

@ -336,7 +336,7 @@ pub fn initialize_workspace(
let edit_prediction_button = cx.new(|cx| { let edit_prediction_button = cx.new(|cx| {
inline_completion_button::InlineCompletionButton::new( inline_completion_button::InlineCompletionButton::new(
app_state.fs.clone(), app_state.fs.clone(),
app_state.user_store.clone(), app_state.cloud_user_store.clone(),
inline_completion_menu_handle.clone(), inline_completion_menu_handle.clone(),
cx, cx,
) )

View file

@ -16,7 +16,7 @@ pub use rate_completion_modal::*;
use anyhow::{Context as _, Result, anyhow}; use anyhow::{Context as _, Result, anyhow};
use arrayvec::ArrayVec; use arrayvec::ArrayVec;
use client::{Client, CloudUserStore, EditPredictionUsage, UserStore}; use client::{Client, CloudUserStore, EditPredictionUsage};
use cloud_llm_client::{ use cloud_llm_client::{
AcceptEditPredictionBody, EXPIRED_LLM_TOKEN_HEADER_NAME, MINIMUM_REQUIRED_VERSION_HEADER_NAME, AcceptEditPredictionBody, EXPIRED_LLM_TOKEN_HEADER_NAME, MINIMUM_REQUIRED_VERSION_HEADER_NAME,
PredictEditsBody, PredictEditsResponse, ZED_VERSION_HEADER_NAME, PredictEditsBody, PredictEditsResponse, ZED_VERSION_HEADER_NAME,
@ -120,10 +120,11 @@ impl Dismissable for ZedPredictUpsell {
} }
} }
pub fn should_show_upsell_modal(user_store: &Entity<UserStore>, cx: &App) -> bool { pub fn should_show_upsell_modal(cloud_user_store: &Entity<CloudUserStore>, cx: &App) -> bool {
match user_store.read(cx).current_user_has_accepted_terms() { if cloud_user_store.read(cx).has_accepted_tos() {
Some(true) => !ZedPredictUpsell::dismissed(), !ZedPredictUpsell::dismissed()
Some(false) | None => true, } else {
true
} }
} }
@ -1804,6 +1805,7 @@ fn tokens_for_bytes(bytes: usize) -> usize {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use client::UserStore;
use client::test::FakeServer; use client::test::FakeServer;
use clock::FakeSystemClock; use clock::FakeSystemClock;
use cloud_api_types::{ use cloud_api_types::{