inline_completion_button: Replace `UserStore` with `CloudUserStore` (#35456)

Marshall Bowers created

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

Release Notes:

- N/A

Change summary

crates/client/src/user.rs                                       |  8 
crates/inline_completion_button/src/inline_completion_button.rs | 29 +-
crates/zed/src/zed.rs                                           |  2 
crates/zeta/src/zeta.rs                                         | 12 
4 files changed, 24 insertions(+), 27 deletions(-)

Detailed changes

crates/client/src/user.rs 🔗

@@ -114,7 +114,6 @@ pub struct UserStore {
     trial_started_at: Option<DateTime<Utc>>,
     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>>,
@@ -190,7 +189,6 @@ impl UserStore {
             trial_started_at: 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(),
@@ -358,7 +356,6 @@ 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;
 
             cx.emit(Event::PlanUpdated);
             cx.notify();
@@ -755,11 +752,6 @@ impl UserStore {
         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())

crates/inline_completion_button/src/inline_completion_button.rs 🔗

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

crates/zed/src/zed.rs 🔗

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

crates/zeta/src/zeta.rs 🔗

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