Added erorr states and first-pass error handling to the copilot status bar item.

Added correct icons
Added a new 'Toast' action which allows other crates to easily pop toasts with an optional click action
This commit is contained in:
Mikayla Maki 2023-03-30 14:10:57 -07:00
parent f235d9f411
commit f5d4bcd934
9 changed files with 279 additions and 73 deletions

View file

@ -97,7 +97,7 @@ impl Workspace {
let notification = build_notification(cx);
cx.subscribe(&notification, move |this, handle, event, cx| {
if handle.read(cx).should_dismiss_notification_on_event(event) {
this.dismiss_notification(type_id, id, cx);
this.dismiss_notification_internal(type_id, id, cx);
}
})
.detach();
@ -107,7 +107,18 @@ impl Workspace {
}
}
fn dismiss_notification(&mut self, type_id: TypeId, id: usize, cx: &mut ViewContext<Self>) {
pub fn dismiss_notification<V: Notification>(&mut self, id: usize, cx: &mut ViewContext<Self>) {
let type_id = TypeId::of::<V>();
self.dismiss_notification_internal(type_id, id, cx)
}
fn dismiss_notification_internal(
&mut self,
type_id: TypeId,
id: usize,
cx: &mut ViewContext<Self>,
) {
self.notifications
.retain(|(existing_type_id, existing_id, _)| {
if (*existing_type_id, *existing_id) == (type_id, id) {
@ -183,6 +194,18 @@ pub mod simple_message_notification {
}
}
pub fn new_boxed_action<S1: Into<Cow<'static, str>>, S2: Into<Cow<'static, str>>>(
message: S1,
click_action: Box<dyn Action>,
click_message: S2,
) -> Self {
Self {
message: message.into(),
click_action: Some(click_action),
click_message: Some(click_message.into()),
}
}
pub fn new<S1: Into<Cow<'static, str>>, A: Action, S2: Into<Cow<'static, str>>>(
message: S1,
click_action: A,
@ -270,9 +293,13 @@ pub mod simple_message_notification {
let style = theme.action_message.style_for(state, false);
if let Some(click_message) = click_message {
Some(
Text::new(click_message, style.text.clone())
.contained()
.with_style(style.container)
Flex::row()
.with_child(
Text::new(click_message, style.text.clone())
.contained()
.with_style(style.container)
.boxed(),
)
.boxed(),
)
} else {
@ -288,7 +315,8 @@ pub mod simple_message_notification {
.on_up(MouseButton::Left, |_, _| {})
.on_click(MouseButton::Left, move |_, cx| {
if let Some(click_action) = click_action.as_ref() {
cx.dispatch_any_action(click_action.boxed_clone())
cx.dispatch_any_action(click_action.boxed_clone());
cx.dispatch_action(CancelMessageNotification)
}
})
.with_cursor_style(if has_click_action {

View file

@ -41,10 +41,10 @@ use gpui::{
impl_actions, impl_internal_actions,
keymap_matcher::KeymapContext,
platform::{CursorStyle, WindowOptions},
AnyModelHandle, AnyViewHandle, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle,
MouseButton, MutableAppContext, PathPromptOptions, Platform, PromptLevel, RenderContext,
SizeConstraint, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle,
WindowBounds,
Action, AnyModelHandle, AnyViewHandle, AppContext, AsyncAppContext, Entity, ModelContext,
ModelHandle, MouseButton, MutableAppContext, PathPromptOptions, Platform, PromptLevel,
RenderContext, SizeConstraint, Subscription, Task, View, ViewContext, ViewHandle,
WeakViewHandle, WindowBounds,
};
use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ProjectItem};
use language::LanguageRegistry;
@ -165,6 +165,67 @@ pub struct OpenProjectEntryInPane {
project_entry: ProjectEntryId,
}
pub struct Toast {
id: usize,
msg: Cow<'static, str>,
click: Option<(Cow<'static, str>, Box<dyn Action>)>,
}
impl Toast {
pub fn new<I: Into<Cow<'static, str>>>(id: usize, msg: I) -> Self {
Toast {
id,
msg: msg.into(),
click: None,
}
}
pub fn new_action<I1: Into<Cow<'static, str>>, I2: Into<Cow<'static, str>>>(
id: usize,
msg: I1,
click_msg: I2,
action: impl Action,
) -> Self {
Toast {
id,
msg: msg.into(),
click: Some((click_msg.into(), Box::new(action))),
}
}
}
impl PartialEq for Toast {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
&& self.msg == other.msg
&& self.click.is_some() == other.click.is_some()
}
}
impl Clone for Toast {
fn clone(&self) -> Self {
Toast {
id: self.id,
msg: self.msg.to_owned(),
click: self
.click
.as_ref()
.map(|(msg, click)| (msg.to_owned(), click.boxed_clone())),
}
}
}
#[derive(Clone, PartialEq)]
pub struct DismissToast {
id: usize,
}
impl DismissToast {
pub fn new(id: usize) -> Self {
DismissToast { id }
}
}
pub type WorkspaceId = i64;
impl_internal_actions!(
@ -178,6 +239,8 @@ impl_internal_actions!(
SplitWithItem,
SplitWithProjectEntry,
OpenProjectEntryInPane,
Toast,
DismissToast
]
);
impl_actions!(workspace, [ActivatePane]);
@ -353,6 +416,24 @@ pub fn init(app_state: Arc<AppState>, cx: &mut MutableAppContext) {
.detach();
});
cx.add_action(|workspace: &mut Workspace, alert: &Toast, cx| {
workspace.dismiss_notification::<MessageNotification>(alert.id, cx);
workspace.show_notification(alert.id, cx, |cx| {
cx.add_view(|_cx| match &alert.click {
Some((click_msg, action)) => MessageNotification::new_boxed_action(
alert.msg.clone(),
action.boxed_clone(),
click_msg.clone(),
),
None => MessageNotification::new_message(alert.msg.clone()),
})
})
});
cx.add_action(|workspace: &mut Workspace, alert: &DismissToast, cx| {
workspace.dismiss_notification::<MessageNotification>(alert.id, cx);
});
let client = &app_state.client;
client.add_view_request_handler(Workspace::handle_follow);
client.add_view_message_handler(Workspace::handle_unfollow);