Improve layout and styling of contact notifications

Co-authored-by: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Max Brunsfeld 2022-05-11 14:20:05 -07:00
parent 3bc9b8ec85
commit 0ba656aa0e
18 changed files with 152 additions and 66 deletions

View file

Before

Width:  |  Height:  |  Size: 184 B

After

Width:  |  Height:  |  Size: 184 B

View file

@ -505,7 +505,7 @@
} }
}, },
"notifications": { "notifications": {
"width": 256, "width": 380,
"margin": { "margin": {
"right": 10, "right": 10,
"bottom": 10 "bottom": 10
@ -1698,7 +1698,8 @@
"color": "#e2dfe7", "color": "#e2dfe7",
"size": 12, "size": 12,
"margin": { "margin": {
"left": 4 "left": 8,
"right": 8
} }
}, },
"header_height": 18, "header_height": 18,
@ -1707,6 +1708,7 @@
"color": "#8b8792", "color": "#8b8792",
"size": 12, "size": 12,
"margin": { "margin": {
"left": 20,
"top": 6, "top": 6,
"bottom": 6 "bottom": 6
} }
@ -1720,6 +1722,9 @@
"corner_radius": 6, "corner_radius": 6,
"margin": { "margin": {
"left": 6 "left": 6
},
"hover": {
"background": "#26232a3d"
} }
}, },
"dismiss_button": { "dismiss_button": {
@ -1727,7 +1732,10 @@
"icon_width": 8, "icon_width": 8,
"icon_height": 8, "icon_height": 8,
"button_width": 8, "button_width": 8,
"button_height": 8 "button_height": 8,
"hover": {
"color": "#e2dfe7"
}
} }
} }
} }

View file

@ -505,7 +505,7 @@
} }
}, },
"notifications": { "notifications": {
"width": 256, "width": 380,
"margin": { "margin": {
"right": 10, "right": 10,
"bottom": 10 "bottom": 10
@ -1698,7 +1698,8 @@
"color": "#26232a", "color": "#26232a",
"size": 12, "size": 12,
"margin": { "margin": {
"left": 4 "left": 8,
"right": 8
} }
}, },
"header_height": 18, "header_height": 18,
@ -1707,6 +1708,7 @@
"color": "#585260", "color": "#585260",
"size": 12, "size": 12,
"margin": { "margin": {
"left": 20,
"top": 6, "top": 6,
"bottom": 6 "bottom": 6
} }
@ -1720,6 +1722,9 @@
"corner_radius": 6, "corner_radius": 6,
"margin": { "margin": {
"left": 6 "left": 6
},
"hover": {
"background": "#e2dfe71f"
} }
}, },
"dismiss_button": { "dismiss_button": {
@ -1727,7 +1732,10 @@
"icon_width": 8, "icon_width": 8,
"icon_height": 8, "icon_height": 8,
"button_width": 8, "button_width": 8,
"button_height": 8 "button_height": 8,
"hover": {
"color": "#26232a"
}
} }
} }
} }

View file

@ -505,7 +505,7 @@
} }
}, },
"notifications": { "notifications": {
"width": 256, "width": 380,
"margin": { "margin": {
"right": 10, "right": 10,
"bottom": 10 "bottom": 10
@ -1698,7 +1698,8 @@
"color": "#f1f1f1", "color": "#f1f1f1",
"size": 12, "size": 12,
"margin": { "margin": {
"left": 4 "left": 8,
"right": 8
} }
}, },
"header_height": 18, "header_height": 18,
@ -1707,6 +1708,7 @@
"color": "#9c9c9c", "color": "#9c9c9c",
"size": 12, "size": 12,
"margin": { "margin": {
"left": 20,
"top": 6, "top": 6,
"bottom": 6 "bottom": 6
} }
@ -1720,6 +1722,9 @@
"corner_radius": 6, "corner_radius": 6,
"margin": { "margin": {
"left": 6 "left": 6
},
"hover": {
"background": "#070707"
} }
}, },
"dismiss_button": { "dismiss_button": {
@ -1727,7 +1732,10 @@
"icon_width": 8, "icon_width": 8,
"icon_height": 8, "icon_height": 8,
"button_width": 8, "button_width": 8,
"button_height": 8 "button_height": 8,
"hover": {
"color": "#c6c6c6"
}
} }
} }
} }

View file

@ -505,7 +505,7 @@
} }
}, },
"notifications": { "notifications": {
"width": 256, "width": 380,
"margin": { "margin": {
"right": 10, "right": 10,
"bottom": 10 "bottom": 10
@ -1698,7 +1698,8 @@
"color": "#2b2b2b", "color": "#2b2b2b",
"size": 12, "size": 12,
"margin": { "margin": {
"left": 4 "left": 8,
"right": 8
} }
}, },
"header_height": 18, "header_height": 18,
@ -1707,6 +1708,7 @@
"color": "#474747", "color": "#474747",
"size": 12, "size": 12,
"margin": { "margin": {
"left": 20,
"top": 6, "top": 6,
"bottom": 6 "bottom": 6
} }
@ -1720,6 +1722,9 @@
"corner_radius": 6, "corner_radius": 6,
"margin": { "margin": {
"left": 6 "left": 6
},
"hover": {
"background": "#e3e3e3"
} }
}, },
"dismiss_button": { "dismiss_button": {
@ -1727,7 +1732,10 @@
"icon_width": 8, "icon_width": 8,
"icon_height": 8, "icon_height": 8,
"button_width": 8, "button_width": 8,
"button_height": 8 "button_height": 8,
"hover": {
"color": "#393939"
}
} }
} }
} }

View file

@ -505,7 +505,7 @@
} }
}, },
"notifications": { "notifications": {
"width": 256, "width": 380,
"margin": { "margin": {
"right": 10, "right": 10,
"bottom": 10 "bottom": 10
@ -1698,7 +1698,8 @@
"color": "#eee8d5", "color": "#eee8d5",
"size": 12, "size": 12,
"margin": { "margin": {
"left": 4 "left": 8,
"right": 8
} }
}, },
"header_height": 18, "header_height": 18,
@ -1707,6 +1708,7 @@
"color": "#93a1a1", "color": "#93a1a1",
"size": 12, "size": 12,
"margin": { "margin": {
"left": 20,
"top": 6, "top": 6,
"bottom": 6 "bottom": 6
} }
@ -1720,6 +1722,9 @@
"corner_radius": 6, "corner_radius": 6,
"margin": { "margin": {
"left": 6 "left": 6
},
"hover": {
"background": "#0736423d"
} }
}, },
"dismiss_button": { "dismiss_button": {
@ -1727,7 +1732,10 @@
"icon_width": 8, "icon_width": 8,
"icon_height": 8, "icon_height": 8,
"button_width": 8, "button_width": 8,
"button_height": 8 "button_height": 8,
"hover": {
"color": "#eee8d5"
}
} }
} }
} }

View file

@ -505,7 +505,7 @@
} }
}, },
"notifications": { "notifications": {
"width": 256, "width": 380,
"margin": { "margin": {
"right": 10, "right": 10,
"bottom": 10 "bottom": 10
@ -1698,7 +1698,8 @@
"color": "#073642", "color": "#073642",
"size": 12, "size": 12,
"margin": { "margin": {
"left": 4 "left": 8,
"right": 8
} }
}, },
"header_height": 18, "header_height": 18,
@ -1707,6 +1708,7 @@
"color": "#586e75", "color": "#586e75",
"size": 12, "size": 12,
"margin": { "margin": {
"left": 20,
"top": 6, "top": 6,
"bottom": 6 "bottom": 6
} }
@ -1720,6 +1722,9 @@
"corner_radius": 6, "corner_radius": 6,
"margin": { "margin": {
"left": 6 "left": 6
},
"hover": {
"background": "#eee8d51f"
} }
}, },
"dismiss_button": { "dismiss_button": {
@ -1727,7 +1732,10 @@
"icon_width": 8, "icon_width": 8,
"icon_height": 8, "icon_height": 8,
"button_width": 8, "button_width": 8,
"button_height": 8 "button_height": 8,
"hover": {
"color": "#073642"
}
} }
} }
} }

View file

@ -505,7 +505,7 @@
} }
}, },
"notifications": { "notifications": {
"width": 256, "width": 380,
"margin": { "margin": {
"right": 10, "right": 10,
"bottom": 10 "bottom": 10
@ -1698,7 +1698,8 @@
"color": "#dfe2f1", "color": "#dfe2f1",
"size": 12, "size": 12,
"margin": { "margin": {
"left": 4 "left": 8,
"right": 8
} }
}, },
"header_height": 18, "header_height": 18,
@ -1707,6 +1708,7 @@
"color": "#979db4", "color": "#979db4",
"size": 12, "size": 12,
"margin": { "margin": {
"left": 20,
"top": 6, "top": 6,
"bottom": 6 "bottom": 6
} }
@ -1720,6 +1722,9 @@
"corner_radius": 6, "corner_radius": 6,
"margin": { "margin": {
"left": 6 "left": 6
},
"hover": {
"background": "#2932563d"
} }
}, },
"dismiss_button": { "dismiss_button": {
@ -1727,7 +1732,10 @@
"icon_width": 8, "icon_width": 8,
"icon_height": 8, "icon_height": 8,
"button_width": 8, "button_width": 8,
"button_height": 8 "button_height": 8,
"hover": {
"color": "#dfe2f1"
}
} }
} }
} }

View file

@ -505,7 +505,7 @@
} }
}, },
"notifications": { "notifications": {
"width": 256, "width": 380,
"margin": { "margin": {
"right": 10, "right": 10,
"bottom": 10 "bottom": 10
@ -1698,7 +1698,8 @@
"color": "#293256", "color": "#293256",
"size": 12, "size": 12,
"margin": { "margin": {
"left": 4 "left": 8,
"right": 8
} }
}, },
"header_height": 18, "header_height": 18,
@ -1707,6 +1708,7 @@
"color": "#5e6687", "color": "#5e6687",
"size": 12, "size": 12,
"margin": { "margin": {
"left": 20,
"top": 6, "top": 6,
"bottom": 6 "bottom": 6
} }
@ -1720,6 +1722,9 @@
"corner_radius": 6, "corner_radius": 6,
"margin": { "margin": {
"left": 6 "left": 6
},
"hover": {
"background": "#dfe2f11f"
} }
}, },
"dismiss_button": { "dismiss_button": {
@ -1727,7 +1732,10 @@
"icon_width": 8, "icon_width": 8,
"icon_height": 8, "icon_height": 8,
"button_width": 8, "button_width": 8,
"button_height": 8 "button_height": 8,
"hover": {
"color": "#293256"
}
} }
} }
} }

View file

@ -371,7 +371,7 @@ impl UserStore {
response: if accept { response: if accept {
proto::ContactRequestResponse::Accept proto::ContactRequestResponse::Accept
} else { } else {
proto::ContactRequestResponse::Reject proto::ContactRequestResponse::Decline
} as i32, } as i32,
}, },
cx, cx,

View file

@ -118,7 +118,7 @@ impl PickerDelegate for ContactFinder {
"icons/accept.svg" "icons/accept.svg"
} }
ContactRequestStatus::RequestSent | ContactRequestStatus::RequestAccepted => { ContactRequestStatus::RequestSent | ContactRequestStatus::RequestAccepted => {
"icons/reject.svg" "icons/decline.svg"
} }
}; };
let button_style = if self.user_store.read(cx).is_contact_request_pending(&user) { let button_style = if self.user_store.read(cx).is_contact_request_pending(&user) {

View file

@ -6,6 +6,8 @@ use gpui::{
use settings::Settings; use settings::Settings;
use workspace::Notification; use workspace::Notification;
use crate::render_icon_button;
impl_internal_actions!(contact_notifications, [Dismiss, RespondToContactRequest]); impl_internal_actions!(contact_notifications, [Dismiss, RespondToContactRequest]);
pub fn init(cx: &mut MutableAppContext) { pub fn init(cx: &mut MutableAppContext) {
@ -31,7 +33,7 @@ pub enum Event {
Dismiss, Dismiss,
} }
enum Reject {} enum Decline {}
enum Accept {} enum Accept {}
impl Entity for ContactNotification { impl Entity for ContactNotification {
@ -87,7 +89,7 @@ impl ContactNotification {
let user_id = user.id; let user_id = user.id;
Flex::column() Flex::column()
.with_child(self.render_header("added you", theme, cx)) .with_child(self.render_header("wants to add you as a contact.", theme, cx))
.with_child( .with_child(
Label::new( Label::new(
"They won't know if you decline.".to_string(), "They won't know if you decline.".to_string(),
@ -100,13 +102,14 @@ impl ContactNotification {
.with_child( .with_child(
Flex::row() Flex::row()
.with_child( .with_child(
MouseEventHandler::new::<Reject, _, _>( MouseEventHandler::new::<Decline, _, _>(
self.event.user.id as usize, self.event.user.id as usize,
cx, cx,
|_, _| { |state, _| {
Label::new("Reject".to_string(), theme.button.text.clone()) let button = theme.button.style_for(state, false);
Label::new("Decline".to_string(), button.text.clone())
.contained() .contained()
.with_style(theme.button.container) .with_style(button.container)
.boxed() .boxed()
}, },
) )
@ -120,10 +123,11 @@ impl ContactNotification {
.boxed(), .boxed(),
) )
.with_child( .with_child(
MouseEventHandler::new::<Accept, _, _>(user.id as usize, cx, |_, _| { MouseEventHandler::new::<Accept, _, _>(user.id as usize, cx, |state, _| {
Label::new("Accept".to_string(), theme.button.text.clone()) let button = theme.button.style_for(state, false);
Label::new("Accept".to_string(), button.text.clone())
.contained() .contained()
.with_style(theme.button.container) .with_style(button.container)
.boxed() .boxed()
}) })
.with_cursor_style(CursorStyle::PointingHand) .with_cursor_style(CursorStyle::PointingHand)
@ -163,42 +167,51 @@ impl ContactNotification {
Image::new(avatar) Image::new(avatar)
.with_style(theme.header_avatar) .with_style(theme.header_avatar)
.aligned() .aligned()
.left() .constrained()
.with_height(
cx.font_cache()
.line_height(theme.header_message.text.font_size),
)
.aligned()
.top()
.boxed() .boxed()
})) }))
.with_child( .with_child(
Label::new( Text::new(
format!("{} {}", user.github_login, message), format!("{} {}", user.github_login, message),
theme.header_message.text.clone(), theme.header_message.text.clone(),
) )
.contained() .contained()
.with_style(theme.header_message.container) .with_style(theme.header_message.container)
.aligned() .aligned()
.top()
.left()
.flex(1., true)
.boxed(), .boxed(),
) )
.with_child( .with_child(
MouseEventHandler::new::<Dismiss, _, _>(user.id as usize, cx, |_, _| { MouseEventHandler::new::<Dismiss, _, _>(user.id as usize, cx, |state, _| {
Svg::new("icons/reject.svg") render_icon_button(
.with_color(theme.dismiss_button.color) theme.dismiss_button.style_for(state, false),
.constrained() "icons/decline.svg",
.with_width(theme.dismiss_button.icon_width) )
.aligned() .boxed()
.contained()
.with_style(theme.dismiss_button.container)
.constrained()
.with_width(theme.dismiss_button.button_width)
.with_height(theme.dismiss_button.button_width)
.aligned()
.boxed()
}) })
.with_cursor_style(CursorStyle::PointingHand) .with_cursor_style(CursorStyle::PointingHand)
.with_padding(Padding::uniform(5.))
.on_click(move |_, cx| cx.dispatch_action(Dismiss(user_id))) .on_click(move |_, cx| cx.dispatch_action(Dismiss(user_id)))
.aligned()
.constrained()
.with_height(
cx.font_cache()
.line_height(theme.header_message.text.font_size),
)
.aligned()
.top()
.flex_float() .flex_float()
.boxed(), .boxed(),
) )
.constrained() .named("contact notification header")
.with_height(theme.header_height)
.boxed()
} }
fn dismiss(&mut self, _: &Dismiss, cx: &mut ViewContext<Self>) { fn dismiss(&mut self, _: &Dismiss, cx: &mut ViewContext<Self>) {

View file

@ -344,7 +344,7 @@ impl ContactsPanel {
is_incoming: bool, is_incoming: bool,
cx: &mut LayoutContext, cx: &mut LayoutContext,
) -> ElementBox { ) -> ElementBox {
enum Reject {} enum Decline {}
enum Accept {} enum Accept {}
enum Cancel {} enum Cancel {}
@ -373,13 +373,13 @@ impl ContactsPanel {
if is_incoming { if is_incoming {
row.add_children([ row.add_children([
MouseEventHandler::new::<Reject, _, _>(user.id as usize, cx, |mouse_state, _| { MouseEventHandler::new::<Decline, _, _>(user.id as usize, cx, |mouse_state, _| {
let button_style = if is_contact_request_pending { let button_style = if is_contact_request_pending {
&theme.disabled_contact_button &theme.disabled_contact_button
} else { } else {
&theme.contact_button.style_for(mouse_state, false) &theme.contact_button.style_for(mouse_state, false)
}; };
render_icon_button(button_style, "icons/reject.svg") render_icon_button(button_style, "icons/decline.svg")
.aligned() .aligned()
.flex_float() .flex_float()
.boxed() .boxed()
@ -421,7 +421,7 @@ impl ContactsPanel {
} else { } else {
&theme.contact_button.style_for(mouse_state, false) &theme.contact_button.style_for(mouse_state, false)
}; };
render_icon_button(button_style, "icons/reject.svg") render_icon_button(button_style, "icons/decline.svg")
.aligned() .aligned()
.flex_float() .flex_float()
.boxed() .boxed()

View file

@ -564,7 +564,7 @@ message RespondToContactRequest {
enum ContactRequestResponse { enum ContactRequestResponse {
Accept = 0; Accept = 0;
Reject = 1; Decline = 1;
Block = 2; Block = 2;
Dismiss = 3; Dismiss = 3;
} }

View file

@ -362,8 +362,8 @@ pub struct ContactNotification {
pub header_message: ContainedText, pub header_message: ContainedText,
pub header_height: f32, pub header_height: f32,
pub body_message: ContainedText, pub body_message: ContainedText,
pub button: ContainedText, pub button: Interactive<ContainedText>,
pub dismiss_button: IconButton, pub dismiss_button: Interactive<IconButton>,
} }
#[derive(Clone, Deserialize, Default)] #[derive(Clone, Deserialize, Default)]

View file

@ -1769,7 +1769,7 @@ impl Workspace {
.boxed() .boxed()
})) }))
.constrained() .constrained()
.with_width(250.) .with_width(theme.notifications.width)
.contained() .contained()
.with_style(theme.notifications.container) .with_style(theme.notifications.container)
.aligned() .aligned()

View file

@ -1,21 +1,24 @@
import Theme from "../themes/theme"; import Theme from "../themes/theme";
import { backgroundColor, iconColor, text } from "./components"; import { backgroundColor, iconColor, text } from "./components";
const avatarSize = 12;
const headerPadding = 8;
export default function contactNotification(theme: Theme): Object { export default function contactNotification(theme: Theme): Object {
return { return {
headerAvatar: { headerAvatar: {
height: 12, height: avatarSize,
width: 12, width: avatarSize,
cornerRadius: 6, cornerRadius: 6,
}, },
headerMessage: { headerMessage: {
...text(theme, "sans", "primary", { size: "xs" }), ...text(theme, "sans", "primary", { size: "xs" }),
margin: { left: 4 } margin: { left: headerPadding, right: headerPadding }
}, },
headerHeight: 18, headerHeight: 18,
bodyMessage: { bodyMessage: {
...text(theme, "sans", "secondary", { size: "xs" }), ...text(theme, "sans", "secondary", { size: "xs" }),
margin: { top: 6, bottom: 6 }, margin: { left: avatarSize + headerPadding, top: 6, bottom: 6 },
}, },
button: { button: {
...text(theme, "sans", "primary", { size: "xs" }), ...text(theme, "sans", "primary", { size: "xs" }),
@ -23,6 +26,9 @@ export default function contactNotification(theme: Theme): Object {
padding: 4, padding: 4,
cornerRadius: 6, cornerRadius: 6,
margin: { left: 6 }, margin: { left: 6 },
hover: {
background: backgroundColor(theme, "on300", "hovered")
}
}, },
dismissButton: { dismissButton: {
color: iconColor(theme, "secondary"), color: iconColor(theme, "secondary"),
@ -30,6 +36,9 @@ export default function contactNotification(theme: Theme): Object {
iconHeight: 8, iconHeight: 8,
buttonWidth: 8, buttonWidth: 8,
buttonHeight: 8, buttonHeight: 8,
hover: {
color: iconColor(theme, "primary")
}
} }
} }
} }

View file

@ -159,7 +159,7 @@ export default function workspace(theme: Theme) {
shadow: shadow(theme), shadow: shadow(theme),
}, },
notifications: { notifications: {
width: 256, width: 380,
margin: { right: 10, bottom: 10 }, margin: { right: 10, bottom: 10 },
} }
}; };