Rework git toasts (#26420)
The notifications from git output could take up variable amounts of screen space, and they were quite obnoxious when a git command printed lots of output, such as fetching many new branches or verbose push hooks. This change makes the push/pull/fetch buttons trigger a small notification toast, based on the output of the command that was ran. For errors or commands with more output the user may want to see, there's an "Open Log" button which opens a new buffer with the output of that command. It also uses this behavior for long error notifications for other git commands like `commit` and `checkout`. The output of those commands can be quite long due to arbitrary githooks running. Release Notes: - N/A --------- Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
This commit is contained in:
parent
e8208643bb
commit
2b94a35aaa
14 changed files with 415 additions and 418 deletions
|
@ -1,15 +1,8 @@
|
|||
use std::sync::Arc;
|
||||
use std::rc::Rc;
|
||||
|
||||
use gpui::{ClickEvent, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, IntoElement};
|
||||
use ui::prelude::*;
|
||||
use workspace::ToastView;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ToastAction {
|
||||
id: ElementId,
|
||||
label: SharedString,
|
||||
on_click: Option<Arc<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
|
||||
}
|
||||
use gpui::{DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, IntoElement};
|
||||
use ui::{prelude::*, Tooltip};
|
||||
use workspace::{ToastAction, ToastView};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct ToastIcon {
|
||||
|
@ -40,49 +33,33 @@ impl From<IconName> for ToastIcon {
|
|||
}
|
||||
}
|
||||
|
||||
impl ToastAction {
|
||||
pub fn new(
|
||||
label: SharedString,
|
||||
on_click: Option<Arc<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
|
||||
) -> Self {
|
||||
let id = ElementId::Name(label.clone());
|
||||
|
||||
Self {
|
||||
id,
|
||||
label,
|
||||
on_click,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(IntoComponent)]
|
||||
#[component(scope = "Notification")]
|
||||
pub struct StatusToast {
|
||||
icon: Option<ToastIcon>,
|
||||
text: SharedString,
|
||||
action: Option<ToastAction>,
|
||||
this_handle: Entity<Self>,
|
||||
focus_handle: FocusHandle,
|
||||
}
|
||||
|
||||
impl StatusToast {
|
||||
pub fn new(
|
||||
text: impl Into<SharedString>,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
f: impl FnOnce(Self, &mut Window, &mut Context<Self>) -> Self,
|
||||
f: impl FnOnce(Self, &mut Context<Self>) -> Self,
|
||||
) -> Entity<Self> {
|
||||
cx.new(|cx| {
|
||||
let focus_handle = cx.focus_handle();
|
||||
|
||||
window.refresh();
|
||||
f(
|
||||
Self {
|
||||
text: text.into(),
|
||||
icon: None,
|
||||
action: None,
|
||||
this_handle: cx.entity(),
|
||||
focus_handle,
|
||||
},
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
|
@ -96,9 +73,18 @@ impl StatusToast {
|
|||
pub fn action(
|
||||
mut self,
|
||||
label: impl Into<SharedString>,
|
||||
f: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
|
||||
f: impl Fn(&mut Window, &mut App) + 'static,
|
||||
) -> Self {
|
||||
self.action = Some(ToastAction::new(label.into(), Some(Arc::new(f))));
|
||||
let this_handle = self.this_handle.clone();
|
||||
self.action = Some(ToastAction::new(
|
||||
label.into(),
|
||||
Some(Rc::new(move |window, cx| {
|
||||
this_handle.update(cx, |_, cx| {
|
||||
cx.emit(DismissEvent);
|
||||
});
|
||||
f(window, cx);
|
||||
})),
|
||||
));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -122,18 +108,24 @@ impl Render for StatusToast {
|
|||
.when_some(self.action.as_ref(), |this, action| {
|
||||
this.child(
|
||||
Button::new(action.id.clone(), action.label.clone())
|
||||
.tooltip(Tooltip::for_action_title(
|
||||
action.label.clone(),
|
||||
&workspace::RunAction,
|
||||
))
|
||||
.color(Color::Muted)
|
||||
.when_some(action.on_click.clone(), |el, handler| {
|
||||
el.on_click(move |click_event, window, cx| {
|
||||
handler(click_event, window, cx)
|
||||
})
|
||||
el.on_click(move |_click_event, window, cx| handler(window, cx))
|
||||
}),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToastView for StatusToast {}
|
||||
impl ToastView for StatusToast {
|
||||
fn action(&self) -> Option<ToastAction> {
|
||||
self.action.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Focusable for StatusToast {
|
||||
fn focus_handle(&self, _cx: &App) -> gpui::FocusHandle {
|
||||
|
@ -144,56 +136,44 @@ impl Focusable for StatusToast {
|
|||
impl EventEmitter<DismissEvent> for StatusToast {}
|
||||
|
||||
impl ComponentPreview for StatusToast {
|
||||
fn preview(window: &mut Window, cx: &mut App) -> AnyElement {
|
||||
let text_example = StatusToast::new("Operation completed", window, cx, |this, _, _| this);
|
||||
fn preview(_window: &mut Window, cx: &mut App) -> AnyElement {
|
||||
let text_example = StatusToast::new("Operation completed", cx, |this, _| this);
|
||||
|
||||
let action_example =
|
||||
StatusToast::new("Update ready to install", window, cx, |this, _, cx| {
|
||||
this.action("Restart", cx.listener(|_, _, _, _| {}))
|
||||
});
|
||||
let action_example = StatusToast::new("Update ready to install", cx, |this, _cx| {
|
||||
this.action("Restart", |_, _| {})
|
||||
});
|
||||
|
||||
let icon_example = StatusToast::new(
|
||||
"Nathan Sobo accepted your contact request",
|
||||
window,
|
||||
cx,
|
||||
|this, _, _| this.icon(ToastIcon::new(IconName::Check).color(Color::Muted)),
|
||||
|this, _| this.icon(ToastIcon::new(IconName::Check).color(Color::Muted)),
|
||||
);
|
||||
|
||||
let success_example = StatusToast::new(
|
||||
"Pushed 4 changes to `zed/main`",
|
||||
window,
|
||||
cx,
|
||||
|this, _, _| this.icon(ToastIcon::new(IconName::Check).color(Color::Success)),
|
||||
);
|
||||
let success_example = StatusToast::new("Pushed 4 changes to `zed/main`", cx, |this, _| {
|
||||
this.icon(ToastIcon::new(IconName::Check).color(Color::Success))
|
||||
});
|
||||
|
||||
let error_example = StatusToast::new(
|
||||
"git push: Couldn't find remote origin `iamnbutler/zed`",
|
||||
window,
|
||||
cx,
|
||||
|this, _, cx| {
|
||||
|this, _cx| {
|
||||
this.icon(ToastIcon::new(IconName::XCircle).color(Color::Error))
|
||||
.action("More Info", cx.listener(|_, _, _, _| {}))
|
||||
.action("More Info", |_, _| {})
|
||||
},
|
||||
);
|
||||
|
||||
let warning_example =
|
||||
StatusToast::new("You have outdated settings", window, cx, |this, _, cx| {
|
||||
this.icon(ToastIcon::new(IconName::Warning).color(Color::Warning))
|
||||
.action("More Info", cx.listener(|_, _, _, _| {}))
|
||||
});
|
||||
let warning_example = StatusToast::new("You have outdated settings", cx, |this, _cx| {
|
||||
this.icon(ToastIcon::new(IconName::Warning).color(Color::Warning))
|
||||
.action("More Info", |_, _| {})
|
||||
});
|
||||
|
||||
let pr_example = StatusToast::new(
|
||||
"`zed/new-notification-system` created!",
|
||||
window,
|
||||
cx,
|
||||
|this, _, cx| {
|
||||
let pr_example =
|
||||
StatusToast::new("`zed/new-notification-system` created!", cx, |this, _cx| {
|
||||
this.icon(ToastIcon::new(IconName::GitBranchSmall).color(Color::Muted))
|
||||
.action(
|
||||
"Open Pull Request",
|
||||
cx.listener(|_, _, _, cx| cx.open_url("https://github.com/")),
|
||||
)
|
||||
},
|
||||
);
|
||||
.action("Open Pull Request", |_, cx| {
|
||||
cx.open_url("https://github.com/")
|
||||
})
|
||||
});
|
||||
|
||||
v_flex()
|
||||
.gap_6()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue