zeta: Show update required notification on appropriate window(s) (#37130)

To show these notifications, Zeta was being initialized with the initial
workspace it's used on - which may not even still exist! This removes a
confusing/misleading workspace field from Zeta.

Release Notes:

- N/A
This commit is contained in:
Michael Sloan
2025-08-29 00:22:56 +00:00
committed by GitHub
parent 960d9ce48c
commit cfd56a744d
2 changed files with 22 additions and 47 deletions
@@ -8,7 +8,6 @@ use settings::SettingsStore;
use std::{cell::RefCell, rc::Rc, sync::Arc}; use std::{cell::RefCell, rc::Rc, sync::Arc};
use supermaven::{Supermaven, SupermavenCompletionProvider}; use supermaven::{Supermaven, SupermavenCompletionProvider};
use ui::Window; use ui::Window;
use workspace::Workspace;
use zeta::{ProviderDataCollection, ZetaEditPredictionProvider}; use zeta::{ProviderDataCollection, ZetaEditPredictionProvider};
pub fn init(client: Arc<Client>, user_store: Entity<UserStore>, cx: &mut App) { pub fn init(client: Arc<Client>, user_store: Entity<UserStore>, cx: &mut App) {
@@ -204,13 +203,7 @@ fn assign_edit_prediction_provider(
} }
} }
let workspace = window let zeta = zeta::Zeta::register(worktree, client.clone(), user_store, cx);
.root::<Workspace>()
.flatten()
.map(|workspace| workspace.downgrade());
let zeta =
zeta::Zeta::register(workspace, worktree, client.clone(), user_store, cx);
if let Some(buffer) = &singleton_buffer if let Some(buffer) = &singleton_buffer
&& buffer.read(cx).file().is_some() && buffer.read(cx).file().is_some()
+21 -39
View File
@@ -24,7 +24,7 @@ use collections::{HashMap, HashSet, VecDeque};
use futures::AsyncReadExt; use futures::AsyncReadExt;
use gpui::{ use gpui::{
App, AppContext as _, AsyncApp, Context, Entity, EntityId, Global, SemanticVersion, App, AppContext as _, AsyncApp, Context, Entity, EntityId, Global, SemanticVersion,
Subscription, Task, WeakEntity, actions, SharedString, Subscription, Task, actions,
}; };
use http_client::{AsyncBody, HttpClient, Method, Request, Response}; use http_client::{AsyncBody, HttpClient, Method, Request, Response};
use input_excerpt::excerpt_for_cursor_position; use input_excerpt::excerpt_for_cursor_position;
@@ -51,8 +51,7 @@ use telemetry_events::EditPredictionRating;
use thiserror::Error; use thiserror::Error;
use util::ResultExt; use util::ResultExt;
use uuid::Uuid; use uuid::Uuid;
use workspace::Workspace; use workspace::notifications::{ErrorMessagePrompt, NotificationId, show_app_notification};
use workspace::notifications::{ErrorMessagePrompt, NotificationId};
use worktree::Worktree; use worktree::Worktree;
const CURSOR_MARKER: &str = "<|user_cursor_is_here|>"; const CURSOR_MARKER: &str = "<|user_cursor_is_here|>";
@@ -212,7 +211,6 @@ impl std::fmt::Debug for EditPrediction {
} }
pub struct Zeta { pub struct Zeta {
workspace: Option<WeakEntity<Workspace>>,
client: Arc<Client>, client: Arc<Client>,
events: VecDeque<Event>, events: VecDeque<Event>,
registered_buffers: HashMap<gpui::EntityId, RegisteredBuffer>, registered_buffers: HashMap<gpui::EntityId, RegisteredBuffer>,
@@ -233,14 +231,13 @@ impl Zeta {
} }
pub fn register( pub fn register(
workspace: Option<WeakEntity<Workspace>>,
worktree: Option<Entity<Worktree>>, worktree: Option<Entity<Worktree>>,
client: Arc<Client>, client: Arc<Client>,
user_store: Entity<UserStore>, user_store: Entity<UserStore>,
cx: &mut App, cx: &mut App,
) -> Entity<Self> { ) -> Entity<Self> {
let this = Self::global(cx).unwrap_or_else(|| { let this = Self::global(cx).unwrap_or_else(|| {
let entity = cx.new(|cx| Self::new(workspace, client, user_store, cx)); let entity = cx.new(|cx| Self::new(client, user_store, cx));
cx.set_global(ZetaGlobal(entity.clone())); cx.set_global(ZetaGlobal(entity.clone()));
entity entity
}); });
@@ -265,19 +262,13 @@ impl Zeta {
self.user_store.read(cx).edit_prediction_usage() self.user_store.read(cx).edit_prediction_usage()
} }
fn new( fn new(client: Arc<Client>, user_store: Entity<UserStore>, cx: &mut Context<Self>) -> Self {
workspace: Option<WeakEntity<Workspace>>,
client: Arc<Client>,
user_store: Entity<UserStore>,
cx: &mut Context<Self>,
) -> Self {
let refresh_llm_token_listener = RefreshLlmTokenListener::global(cx); let refresh_llm_token_listener = RefreshLlmTokenListener::global(cx);
let data_collection_choice = Self::load_data_collection_choices(); let data_collection_choice = Self::load_data_collection_choices();
let data_collection_choice = cx.new(|_| data_collection_choice); let data_collection_choice = cx.new(|_| data_collection_choice);
Self { Self {
workspace,
client, client,
events: VecDeque::new(), events: VecDeque::new(),
shown_completions: VecDeque::new(), shown_completions: VecDeque::new(),
@@ -370,7 +361,6 @@ impl Zeta {
fn request_completion_impl<F, R>( fn request_completion_impl<F, R>(
&mut self, &mut self,
workspace: Option<Entity<Workspace>>,
project: Option<&Entity<Project>>, project: Option<&Entity<Project>>,
buffer: &Entity<Buffer>, buffer: &Entity<Buffer>,
cursor: language::Anchor, cursor: language::Anchor,
@@ -453,23 +443,20 @@ impl Zeta {
zeta.update_required = true; zeta.update_required = true;
}); });
if let Some(workspace) = workspace { let error_message: SharedString = err.to_string().into();
workspace.update(cx, |workspace, cx| { show_app_notification(
workspace.show_notification( NotificationId::unique::<ZedUpdateRequiredError>(),
NotificationId::unique::<ZedUpdateRequiredError>(), cx,
cx, move |cx| {
|cx| { cx.new(|cx| {
cx.new(|cx| { ErrorMessagePrompt::new(error_message.clone(), cx)
ErrorMessagePrompt::new(err.to_string(), cx) .with_link_button(
.with_link_button( "Update Zed",
"Update Zed", "https://zed.dev/releases",
"https://zed.dev/releases", )
) })
}) },
}, );
);
});
}
}) })
.ok(); .ok();
} }
@@ -689,7 +676,7 @@ and then another
) -> Task<Result<Option<EditPrediction>>> { ) -> Task<Result<Option<EditPrediction>>> {
use std::future::ready; use std::future::ready;
self.request_completion_impl(None, project, buffer, position, false, cx, |_params| { self.request_completion_impl(project, buffer, position, false, cx, |_params| {
ready(Ok((response, None))) ready(Ok((response, None)))
}) })
} }
@@ -702,12 +689,7 @@ and then another
can_collect_data: bool, can_collect_data: bool,
cx: &mut Context<Self>, cx: &mut Context<Self>,
) -> Task<Result<Option<EditPrediction>>> { ) -> Task<Result<Option<EditPrediction>>> {
let workspace = self
.workspace
.as_ref()
.and_then(|workspace| workspace.upgrade());
self.request_completion_impl( self.request_completion_impl(
workspace,
project, project,
buffer, buffer,
position, position,
@@ -2029,7 +2011,7 @@ mod tests {
// Construct the fake server to authenticate. // Construct the fake server to authenticate.
let _server = FakeServer::for_client(42, &client, cx).await; let _server = FakeServer::for_client(42, &client, cx).await;
let user_store = cx.new(|cx| UserStore::new(client.clone(), cx)); let user_store = cx.new(|cx| UserStore::new(client.clone(), cx));
let zeta = cx.new(|cx| Zeta::new(None, client, user_store.clone(), cx)); let zeta = cx.new(|cx| Zeta::new(client, user_store.clone(), cx));
let buffer = cx.new(|cx| Buffer::local(buffer_content, cx)); let buffer = cx.new(|cx| Buffer::local(buffer_content, cx));
let cursor = buffer.read_with(cx, |buffer, _| buffer.anchor_before(Point::new(1, 0))); let cursor = buffer.read_with(cx, |buffer, _| buffer.anchor_before(Point::new(1, 0)));
@@ -2093,7 +2075,7 @@ mod tests {
// Construct the fake server to authenticate. // Construct the fake server to authenticate.
let _server = FakeServer::for_client(42, &client, cx).await; let _server = FakeServer::for_client(42, &client, cx).await;
let user_store = cx.new(|cx| UserStore::new(client.clone(), cx)); let user_store = cx.new(|cx| UserStore::new(client.clone(), cx));
let zeta = cx.new(|cx| Zeta::new(None, client, user_store.clone(), cx)); let zeta = cx.new(|cx| Zeta::new(client, user_store.clone(), cx));
let buffer = cx.new(|cx| Buffer::local(buffer_content, cx)); let buffer = cx.new(|cx| Buffer::local(buffer_content, cx));
let snapshot = buffer.read_with(cx, |buffer, _| buffer.snapshot()); let snapshot = buffer.read_with(cx, |buffer, _| buffer.snapshot());