Implement Notifications
Co-Authored-By: Marshall Bowers <1486634+maxdeviant@users.noreply.github.com>
This commit is contained in:
parent
3f74f75dee
commit
be3cc6458c
4 changed files with 110 additions and 100 deletions
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
h_stack, prelude::*, static_new_notification_items, v_stack, Avatar, Button, Icon, IconButton,
|
h_stack, prelude::*, static_new_notification_items, v_stack, Avatar, Button, Icon, IconButton,
|
||||||
IconElement, Label, LabelColor, LineHeightStyle, ListHeaderMeta, ListSeparator, Stack,
|
IconElement, Label, LabelColor, LineHeightStyle, ListHeaderMeta, ListSeparator,
|
||||||
UnreadIndicator,
|
UnreadIndicator,
|
||||||
};
|
};
|
||||||
use crate::{ClickHandler, ListHeader};
|
use crate::{ClickHandler, ListHeader};
|
||||||
|
@ -67,6 +67,18 @@ pub enum ButtonOrIconButton<V: 'static> {
|
||||||
IconButton(IconButton<V>),
|
IconButton(IconButton<V>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<V: 'static> From<Button<V>> for ButtonOrIconButton<V> {
|
||||||
|
fn from(value: Button<V>) -> Self {
|
||||||
|
Self::Button(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: 'static> From<IconButton<V>> for ButtonOrIconButton<V> {
|
||||||
|
fn from(value: IconButton<V>) -> Self {
|
||||||
|
Self::IconButton(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct NotificationAction<V: 'static> {
|
pub struct NotificationAction<V: 'static> {
|
||||||
button: ButtonOrIconButton<V>,
|
button: ButtonOrIconButton<V>,
|
||||||
tooltip: SharedString,
|
tooltip: SharedString,
|
||||||
|
@ -80,19 +92,25 @@ pub struct NotificationAction<V: 'static> {
|
||||||
taken_message: (Option<Icon>, SharedString),
|
taken_message: (Option<Icon>, SharedString),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<V: 'static> NotificationAction<V> {
|
||||||
|
pub fn new(
|
||||||
|
button: impl Into<ButtonOrIconButton<V>>,
|
||||||
|
tooltip: impl Into<SharedString>,
|
||||||
|
(icon, taken_message): (Option<Icon>, impl Into<SharedString>),
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
button: button.into(),
|
||||||
|
tooltip: tooltip.into(),
|
||||||
|
taken_message: (icon, taken_message.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct NotificationWithActions<V: 'static> {
|
pub struct NotificationWithActions<V: 'static> {
|
||||||
notification: Notification<V>,
|
notification: Notification<V>,
|
||||||
actions: [NotificationAction<V>; 2],
|
actions: [NotificationAction<V>; 2],
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a person with a Zed account's public profile.
|
|
||||||
/// All data in this struct should be considered public.
|
|
||||||
pub struct PublicActor {
|
|
||||||
username: SharedString,
|
|
||||||
avatar: SharedString,
|
|
||||||
is_contact: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum ActorOrIcon {
|
pub enum ActorOrIcon {
|
||||||
Actor(PublicActor),
|
Actor(PublicActor),
|
||||||
Icon(Icon),
|
Icon(Icon),
|
||||||
|
@ -162,13 +180,13 @@ impl<V> Notification<V> {
|
||||||
/// Requires a click action.
|
/// Requires a click action.
|
||||||
pub fn new_actor_message(
|
pub fn new_actor_message(
|
||||||
id: impl Into<ElementId>,
|
id: impl Into<ElementId>,
|
||||||
message: SharedString,
|
message: impl Into<SharedString>,
|
||||||
actor: PublicActor,
|
actor: PublicActor,
|
||||||
click_action: ClickHandler<V>,
|
click_action: ClickHandler<V>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::new(
|
Self::new(
|
||||||
id.into(),
|
id.into(),
|
||||||
message,
|
message.into(),
|
||||||
ActorOrIcon::Actor(actor),
|
ActorOrIcon::Actor(actor),
|
||||||
Some(click_action),
|
Some(click_action),
|
||||||
)
|
)
|
||||||
|
@ -179,13 +197,13 @@ impl<V> Notification<V> {
|
||||||
/// Requires a click action.
|
/// Requires a click action.
|
||||||
pub fn new_icon_message(
|
pub fn new_icon_message(
|
||||||
id: impl Into<ElementId>,
|
id: impl Into<ElementId>,
|
||||||
message: SharedString,
|
message: impl Into<SharedString>,
|
||||||
icon: Icon,
|
icon: Icon,
|
||||||
click_action: ClickHandler<V>,
|
click_action: ClickHandler<V>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::new(
|
Self::new(
|
||||||
id.into(),
|
id.into(),
|
||||||
message,
|
message.into(),
|
||||||
ActorOrIcon::Icon(icon),
|
ActorOrIcon::Icon(icon),
|
||||||
Some(click_action),
|
Some(click_action),
|
||||||
)
|
)
|
||||||
|
@ -197,12 +215,11 @@ impl<V> Notification<V> {
|
||||||
/// Cannot take a click action due to required actions.
|
/// Cannot take a click action due to required actions.
|
||||||
pub fn new_actor_with_actions(
|
pub fn new_actor_with_actions(
|
||||||
id: impl Into<ElementId>,
|
id: impl Into<ElementId>,
|
||||||
message: SharedString,
|
message: impl Into<SharedString>,
|
||||||
actor: PublicActor,
|
actor: PublicActor,
|
||||||
click_action: ClickHandler<V>,
|
|
||||||
actions: [NotificationAction<V>; 2],
|
actions: [NotificationAction<V>; 2],
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::new(id.into(), message, ActorOrIcon::Actor(actor), None).actions(actions)
|
Self::new(id.into(), message.into(), ActorOrIcon::Actor(actor), None).actions(actions)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new notification with an icon slot
|
/// Creates a new notification with an icon slot
|
||||||
|
@ -211,12 +228,11 @@ impl<V> Notification<V> {
|
||||||
/// Cannot take a click action due to required actions.
|
/// Cannot take a click action due to required actions.
|
||||||
pub fn new_icon_with_actions(
|
pub fn new_icon_with_actions(
|
||||||
id: impl Into<ElementId>,
|
id: impl Into<ElementId>,
|
||||||
message: SharedString,
|
message: impl Into<SharedString>,
|
||||||
icon: Icon,
|
icon: Icon,
|
||||||
click_action: ClickHandler<V>,
|
|
||||||
actions: [NotificationAction<V>; 2],
|
actions: [NotificationAction<V>; 2],
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::new(id.into(), message, ActorOrIcon::Icon(icon), None).actions(actions)
|
Self::new(id.into(), message.into(), ActorOrIcon::Icon(icon), None).actions(actions)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_click(mut self, handler: ClickHandler<V>) -> Self {
|
fn on_click(mut self, handler: ClickHandler<V>) -> Self {
|
||||||
|
@ -253,45 +269,6 @@ impl<V> Notification<V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_actions(&self, cx: &mut ViewContext<V>) -> impl Component<V> {
|
|
||||||
// match (&self.actions, &self.action_taken) {
|
|
||||||
// // Show nothing
|
|
||||||
// (None, _) => div(),
|
|
||||||
// // Show the taken_message
|
|
||||||
// (Some(_), Some(action_taken)) => h_stack()
|
|
||||||
// .children(
|
|
||||||
// action_taken
|
|
||||||
// .taken_message
|
|
||||||
// .0
|
|
||||||
// .map(|icon| IconElement::new(icon).color(crate::IconColor::Muted)),
|
|
||||||
// )
|
|
||||||
// .child(Label::new(action_taken.taken_message.1.clone()).color(LabelColor::Muted)),
|
|
||||||
// // Show the actions
|
|
||||||
// (Some(actions), None) => h_stack()
|
|
||||||
// .children(actions.iter().map(actiona.ction.tton Component::render(button),Component::render(icon_button))),
|
|
||||||
// }))
|
|
||||||
// .collect::<Vec<_>>(),
|
|
||||||
}
|
|
||||||
|
|
||||||
// if let Some(actions) = &self.actions {
|
|
||||||
// let action_children = actions
|
|
||||||
// .iter()
|
|
||||||
// .map(|action| match &action.button {
|
|
||||||
// ButtonOrIconButton::Button(button) => {
|
|
||||||
// div().class("action_button").child(button.label.clone())
|
|
||||||
// }
|
|
||||||
// ButtonOrIconButton::IconButton(icon_button) => div()
|
|
||||||
// .class("action_icon_button")
|
|
||||||
// .child(icon_button.icon.to_string()),
|
|
||||||
// })
|
|
||||||
// .collect::<Vec<_>>();
|
|
||||||
|
|
||||||
// el = el.child(h_stack().children(action_children));
|
|
||||||
// } else {
|
|
||||||
// el = el.child(h_stack().child(div()));
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_slot(&self, cx: &mut ViewContext<V>) -> impl Component<V> {
|
fn render_slot(&self, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||||
match &self.slot {
|
match &self.slot {
|
||||||
ActorOrIcon::Actor(actor) => Avatar::new(actor.avatar.clone()).render(),
|
ActorOrIcon::Actor(actor) => Avatar::new(actor.avatar.clone()).render(),
|
||||||
|
@ -336,44 +313,30 @@ impl<V> Notification<V> {
|
||||||
)
|
)
|
||||||
.child(self.render_meta_items(cx)),
|
.child(self.render_meta_items(cx)),
|
||||||
)
|
)
|
||||||
.child(
|
.child(match (self.actions, self.action_taken) {
|
||||||
|
|
||||||
match (self.actions, self.action_taken) {
|
|
||||||
// Show nothing
|
// Show nothing
|
||||||
(None, _) => div(),
|
(None, _) => div(),
|
||||||
// Show the taken_message
|
// Show the taken_message
|
||||||
(Some(_), Some(action_taken)) => h_stack()
|
(Some(_), Some(action_taken)) => h_stack()
|
||||||
.children(
|
.children(action_taken.taken_message.0.map(|icon| {
|
||||||
action_taken
|
IconElement::new(icon).color(crate::IconColor::Muted)
|
||||||
.taken_message
|
}))
|
||||||
.0
|
.child(
|
||||||
.map(|icon| IconElement::new(icon).color(crate::IconColor::Muted)),
|
Label::new(action_taken.taken_message.1.clone())
|
||||||
)
|
.color(LabelColor::Muted),
|
||||||
.child(Label::new(action_taken.taken_message.1.clone()).color(LabelColor::Muted)),
|
|
||||||
// Show the actions
|
|
||||||
(Some(actions), None) => h_stack()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// match (&self.actions, &self.action_taken) {
|
|
||||||
// // Show nothing
|
|
||||||
// (None, _) => div(),
|
|
||||||
// // Show the taken_message
|
|
||||||
// (Some(_), Some(action_taken)) => h_stack()
|
|
||||||
// .children(
|
|
||||||
// action_taken
|
|
||||||
// .taken_message
|
|
||||||
// .0
|
|
||||||
// .map(|icon| IconElement::new(icon).color(crate::IconColor::Muted)),
|
|
||||||
// )
|
|
||||||
// .child(Label::new(action_taken.taken_message.1.clone()).color(LabelColor::Muted)),
|
|
||||||
// // Show the actions
|
|
||||||
// (Some(actions), None) => h_stack()
|
|
||||||
// .children(actions.iter().map(actiona.ction.tton Component::render(button),Component::render(icon_button))),
|
|
||||||
// }))
|
|
||||||
// .collect::<Vec<_>>(),
|
|
||||||
|
|
||||||
),
|
),
|
||||||
|
// Show the actions
|
||||||
|
(Some(actions), None) => {
|
||||||
|
h_stack().children(actions.map(|action| match action.button {
|
||||||
|
ButtonOrIconButton::Button(button) => {
|
||||||
|
Component::render(button)
|
||||||
|
}
|
||||||
|
ButtonOrIconButton::IconButton(icon_button) => {
|
||||||
|
Component::render(icon_button)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,7 @@ pub enum Icon {
|
||||||
AudioOff,
|
AudioOff,
|
||||||
AudioOn,
|
AudioOn,
|
||||||
Bolt,
|
Bolt,
|
||||||
|
Check,
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
ChevronLeft,
|
ChevronLeft,
|
||||||
ChevronRight,
|
ChevronRight,
|
||||||
|
@ -105,6 +106,7 @@ impl Icon {
|
||||||
Icon::AudioOff => "icons/speaker-off.svg",
|
Icon::AudioOff => "icons/speaker-off.svg",
|
||||||
Icon::AudioOn => "icons/speaker-loud.svg",
|
Icon::AudioOn => "icons/speaker-loud.svg",
|
||||||
Icon::Bolt => "icons/bolt.svg",
|
Icon::Bolt => "icons/bolt.svg",
|
||||||
|
Icon::Check => "icons/check.svg",
|
||||||
Icon::ChevronDown => "icons/chevron_down.svg",
|
Icon::ChevronDown => "icons/chevron_down.svg",
|
||||||
Icon::ChevronLeft => "icons/chevron_left.svg",
|
Icon::ChevronLeft => "icons/chevron_left.svg",
|
||||||
Icon::ChevronRight => "icons/chevron_right.svg",
|
Icon::ChevronRight => "icons/chevron_right.svg",
|
||||||
|
|
|
@ -19,6 +19,24 @@ pub fn ui_size(cx: &mut WindowContext, size: f32) -> Rems {
|
||||||
rems(*settings.ui_scale * UI_SCALE_RATIO * size)
|
rems(*settings.ui_scale * UI_SCALE_RATIO * size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents a person with a Zed account's public profile.
|
||||||
|
/// All data in this struct should be considered public.
|
||||||
|
pub struct PublicActor {
|
||||||
|
pub username: SharedString,
|
||||||
|
pub avatar: SharedString,
|
||||||
|
pub is_contact: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PublicActor {
|
||||||
|
pub fn new(username: impl Into<SharedString>, avatar: impl Into<SharedString>) -> Self {
|
||||||
|
Self {
|
||||||
|
username: username.into(),
|
||||||
|
avatar: avatar.into(),
|
||||||
|
is_contact: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
|
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
|
||||||
pub enum FileSystemStatus {
|
pub enum FileSystemStatus {
|
||||||
#[default]
|
#[default]
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use gpui2::{AppContext, ViewContext};
|
use gpui2::{AppContext, ViewContext};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
@ -7,12 +8,13 @@ use theme2::ActiveTheme;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Buffer, BufferRow, BufferRows, Button, EditorPane, FileSystemStatus, GitStatus,
|
Buffer, BufferRow, BufferRows, Button, EditorPane, FileSystemStatus, GitStatus,
|
||||||
HighlightedLine, Icon, Keybinding, Label, LabelColor, ListEntry, ListEntrySize, ListHeaderMeta,
|
HighlightedLine, Icon, Keybinding, Label, LabelColor, ListEntry, ListEntrySize, ListSubHeader,
|
||||||
ListItem, ListSubHeader, Livestream, MicStatus, ModifierKeys, NotificationItem, PaletteItem,
|
Livestream, MicStatus, ModifierKeys, Notification, NotificationItem, PaletteItem, Player,
|
||||||
Player, PlayerCallStatus, PlayerWithCallStatus, ScreenShareStatus, Symbol, Tab, ToggleState,
|
PlayerCallStatus, PlayerWithCallStatus, PublicActor, ScreenShareStatus, Symbol, Tab,
|
||||||
VideoStatus,
|
ToggleState, VideoStatus,
|
||||||
};
|
};
|
||||||
use crate::{HighlightedText, ListDetailsEntry};
|
use crate::{HighlightedText, ListDetailsEntry};
|
||||||
|
use crate::{ListItem, NotificationAction};
|
||||||
|
|
||||||
pub fn static_tabs_example() -> Vec<Tab> {
|
pub fn static_tabs_example() -> Vec<Tab> {
|
||||||
vec![
|
vec![
|
||||||
|
@ -327,8 +329,33 @@ pub fn static_players_with_call_status() -> Vec<PlayerWithCallStatus> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn static_new_notification_items_2<V: 'static>() -> Vec<NotificationItem<V>> {
|
pub fn static_new_notification_items_2<V: 'static>() -> Vec<NotificationItem<V>> {
|
||||||
vec![]
|
vec![
|
||||||
|
NotificationItem::Message(Notification::new_icon_message(
|
||||||
|
"notif-1",
|
||||||
|
"You were mentioned in a note.",
|
||||||
|
Icon::AtSign,
|
||||||
|
Arc::new(|_, _| {}),
|
||||||
|
)),
|
||||||
|
NotificationItem::Message(Notification::new_actor_with_actions(
|
||||||
|
"notif-2",
|
||||||
|
"as-cii sent you a contact request.",
|
||||||
|
PublicActor::new("as-cii", "http://github.com/as-cii.png?s=50"),
|
||||||
|
[
|
||||||
|
NotificationAction::new(
|
||||||
|
Button::new("Decline"),
|
||||||
|
"Decline Request",
|
||||||
|
(Some(Icon::XCircle), "Declined"),
|
||||||
|
),
|
||||||
|
NotificationAction::new(
|
||||||
|
Button::new("Accept").variant(crate::ButtonVariant::Filled),
|
||||||
|
"Accept Request",
|
||||||
|
(Some(Icon::Check), "Accepted"),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn static_new_notification_items<V: 'static>() -> Vec<ListItem<V>> {
|
pub fn static_new_notification_items<V: 'static>() -> Vec<ListItem<V>> {
|
||||||
vec![
|
vec![
|
||||||
ListItem::Header(ListSubHeader::new("New")),
|
ListItem::Header(ListSubHeader::new("New")),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue