Show settings file errors on startup (#23817)

Required using a global `LazyLock<Mutex<AppNotifications>>` instead of a
context global because settings errors first occur before initialization
of the notifications global.

Release Notes:

- Errors in settings file are now reported in UI on startup.
This commit is contained in:
Michael Sloan 2025-01-29 00:05:33 -07:00 committed by GitHub
parent 06936c69f6
commit dbdf140ca1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 74 additions and 89 deletions

View file

@ -3,17 +3,12 @@ use gpui::{
svg, AnyView, App, AppContext as _, AsyncWindowContext, ClipboardItem, Context, DismissEvent,
Entity, EventEmitter, Global, PromptLevel, Render, ScrollHandle, Task,
};
use std::rc::Rc;
use parking_lot::Mutex;
use std::sync::{Arc, LazyLock};
use std::{any::TypeId, time::Duration};
use ui::{prelude::*, Tooltip};
use util::ResultExt;
pub fn init(cx: &mut App) {
cx.set_global(GlobalAppNotifications {
app_notifications: Vec::new(),
})
}
#[derive(Debug, PartialEq, Clone)]
pub enum NotificationId {
Unique(TypeId),
@ -162,7 +157,7 @@ impl Workspace {
pub fn show_initial_notifications(&mut self, cx: &mut Context<Self>) {
// Allow absence of the global so that tests don't need to initialize it.
let app_notifications = cx
.try_global::<GlobalAppNotifications>()
.try_global::<AppNotifications>()
.iter()
.flat_map(|global| global.app_notifications.iter().cloned())
.collect::<Vec<_>>();
@ -500,21 +495,27 @@ pub mod simple_message_notification {
}
}
static GLOBAL_APP_NOTIFICATIONS: LazyLock<Mutex<AppNotifications>> = LazyLock::new(|| {
Mutex::new(AppNotifications {
app_notifications: Vec::new(),
})
});
/// Stores app notifications so that they can be shown in new workspaces.
struct GlobalAppNotifications {
struct AppNotifications {
app_notifications: Vec<(
NotificationId,
Rc<dyn Fn(&mut Context<Workspace>) -> AnyView>,
Arc<dyn Fn(&mut Context<Workspace>) -> AnyView + Send + Sync>,
)>,
}
impl Global for GlobalAppNotifications {}
impl Global for AppNotifications {}
impl GlobalAppNotifications {
impl AppNotifications {
pub fn insert(
&mut self,
id: NotificationId,
build_notification: Rc<dyn Fn(&mut Context<Workspace>) -> AnyView>,
build_notification: Arc<dyn Fn(&mut Context<Workspace>) -> AnyView + Send + Sync>,
) {
self.remove(&id);
self.app_notifications.push((id, build_notification))
@ -532,28 +533,30 @@ impl GlobalAppNotifications {
pub fn show_app_notification<V: Notification + 'static>(
id: NotificationId,
cx: &mut App,
build_notification: impl Fn(&mut Context<Workspace>) -> Entity<V> + 'static,
build_notification: impl Fn(&mut Context<Workspace>) -> Entity<V> + 'static + Send + Sync,
) {
// Defer notification creation so that windows on the stack can be returned to GPUI
cx.defer(move |cx| {
// Handle dismiss events by removing the notification from all workspaces.
let build_notification: Rc<dyn Fn(&mut Context<Workspace>) -> AnyView> = Rc::new({
let id = id.clone();
move |cx| {
let notification = build_notification(cx);
cx.subscribe(&notification, {
let id = id.clone();
move |_, _, _: &DismissEvent, cx| {
dismiss_app_notification(&id, cx);
}
})
.detach();
notification.into()
}
});
let build_notification: Arc<dyn Fn(&mut Context<Workspace>) -> AnyView + Send + Sync> =
Arc::new({
let id = id.clone();
move |cx| {
let notification = build_notification(cx);
cx.subscribe(&notification, {
let id = id.clone();
move |_, _, _: &DismissEvent, cx| {
dismiss_app_notification(&id, cx);
}
})
.detach();
notification.into()
}
});
// Store the notification so that new workspaces also receive it.
cx.global_mut::<GlobalAppNotifications>()
GLOBAL_APP_NOTIFICATIONS
.lock()
.insert(id.clone(), build_notification.clone());
for window in cx.windows() {
@ -576,7 +579,7 @@ pub fn dismiss_app_notification(id: &NotificationId, cx: &mut App) {
let id = id.clone();
// Defer notification dismissal so that windows on the stack can be returned to GPUI
cx.defer(move |cx| {
cx.global_mut::<GlobalAppNotifications>().remove(&id);
GLOBAL_APP_NOTIFICATIONS.lock().remove(&id);
for window in cx.windows() {
if let Some(workspace_window) = window.downcast::<Workspace>() {
let id = id.clone();

View file

@ -365,7 +365,6 @@ fn prompt_and_open_paths(app_state: Arc<AppState>, options: PathPromptOptions, c
pub fn init(app_state: Arc<AppState>, cx: &mut App) {
init_settings(cx);
notifications::init(cx);
theme_preview::init(cx);
cx.on_action(Workspace::close_global);