From 4afa5fb23e6c791bde56c23fa6c57a19fb233395 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 8 Jan 2024 21:54:59 -0500 Subject: [PATCH] Add stories for collab notifications (#3967) This PR adds some basic stories for collab notifications to make them easier to work on: Screenshot 2024-01-08 at 9 43 39 PM I factored out a `CollabNotification` component that defines the general structure for one of these notifications, and this is the component that we use in the stories, with representative values passed to it to simulate the different instances of the notification. We can't use the actual notification components in the stories due to their data dependencies. Release Notes: - N/A --- Cargo.lock | 2 + crates/collab_ui/Cargo.toml | 3 + crates/collab_ui/src/notifications.rs | 11 ++- .../src/notifications/collab_notification.rs | 52 +++++++++++++ .../incoming_call_notification.rs | 52 +++++-------- .../project_shared_notification.rs | 74 +++++++------------ crates/collab_ui/src/notifications/stories.rs | 3 + .../stories/collab_notification.rs | 50 +++++++++++++ crates/storybook/Cargo.toml | 1 + crates/storybook/src/story_selector.rs | 4 + 10 files changed, 169 insertions(+), 83 deletions(-) create mode 100644 crates/collab_ui/src/notifications/collab_notification.rs create mode 100644 crates/collab_ui/src/notifications/stories.rs create mode 100644 crates/collab_ui/src/notifications/stories/collab_notification.rs diff --git a/Cargo.lock b/Cargo.lock index 00c13d10ff899169f469d0fbe233b9e75a3649f1..c6e7ecebc1300628462750ed714b9103e6a001c6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1559,6 +1559,7 @@ dependencies = [ "serde_json", "settings", "smallvec", + "story", "theme", "theme_selector", "time", @@ -7447,6 +7448,7 @@ dependencies = [ "backtrace-on-stack-overflow", "chrono", "clap 4.4.4", + "collab_ui", "dialoguer", "editor", "fuzzy", diff --git a/crates/collab_ui/Cargo.toml b/crates/collab_ui/Cargo.toml index f845de3a939886fefa42343131e0c4ec18543fea..84c1810bc841d904a7a534fb562671dc9d7232c9 100644 --- a/crates/collab_ui/Cargo.toml +++ b/crates/collab_ui/Cargo.toml @@ -9,6 +9,8 @@ path = "src/collab_ui.rs" doctest = false [features] +default = [] +stories = ["dep:story"] test-support = [ "call/test-support", "client/test-support", @@ -44,6 +46,7 @@ project = { path = "../project" } recent_projects = { path = "../recent_projects" } rpc = { path = "../rpc" } settings = { path = "../settings" } +story = { path = "../story", optional = true } feature_flags = { path = "../feature_flags"} theme = { path = "../theme" } theme_selector = { path = "../theme_selector" } diff --git a/crates/collab_ui/src/notifications.rs b/crates/collab_ui/src/notifications.rs index 5c184ec5c86ab268f4455b21d855ca118d40d50b..7759fef52059fb47dbc74a3803be309dded114c1 100644 --- a/crates/collab_ui/src/notifications.rs +++ b/crates/collab_ui/src/notifications.rs @@ -1,9 +1,16 @@ +mod collab_notification; +pub mod incoming_call_notification; +pub mod project_shared_notification; + +#[cfg(feature = "stories")] +mod stories; + use gpui::AppContext; use std::sync::Arc; use workspace::AppState; -pub mod incoming_call_notification; -pub mod project_shared_notification; +#[cfg(feature = "stories")] +pub use stories::*; pub fn init(app_state: &Arc, cx: &mut AppContext) { incoming_call_notification::init(app_state, cx); diff --git a/crates/collab_ui/src/notifications/collab_notification.rs b/crates/collab_ui/src/notifications/collab_notification.rs new file mode 100644 index 0000000000000000000000000000000000000000..e2ef06f9a58f8b131f166ab2a83250839f076ca2 --- /dev/null +++ b/crates/collab_ui/src/notifications/collab_notification.rs @@ -0,0 +1,52 @@ +use gpui::{img, prelude::*, AnyElement}; +use smallvec::SmallVec; +use ui::prelude::*; + +#[derive(IntoElement)] +pub struct CollabNotification { + avatar_uri: SharedString, + accept_button: Button, + dismiss_button: Button, + children: SmallVec<[AnyElement; 2]>, +} + +impl CollabNotification { + pub fn new( + avatar_uri: impl Into, + accept_button: Button, + dismiss_button: Button, + ) -> Self { + Self { + avatar_uri: avatar_uri.into(), + accept_button, + dismiss_button, + children: SmallVec::new(), + } + } +} + +impl ParentElement for CollabNotification { + fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> { + &mut self.children + } +} + +impl RenderOnce for CollabNotification { + fn render(self, cx: &mut WindowContext) -> impl IntoElement { + h_stack() + .text_ui() + .justify_between() + .size_full() + .overflow_hidden() + .elevation_3(cx) + .p_2() + .gap_2() + .child(img(self.avatar_uri).w_12().h_12().rounded_full()) + .child(v_stack().overflow_hidden().children(self.children)) + .child( + v_stack() + .child(self.accept_button) + .child(self.dismiss_button), + ) + } +} diff --git a/crates/collab_ui/src/notifications/incoming_call_notification.rs b/crates/collab_ui/src/notifications/incoming_call_notification.rs index fa28ef9a6030d4009e96d170e7f7aefc46e52783..223415119fca00cca478d56423b20f9b44417c2e 100644 --- a/crates/collab_ui/src/notifications/incoming_call_notification.rs +++ b/crates/collab_ui/src/notifications/incoming_call_notification.rs @@ -1,15 +1,12 @@ use crate::notification_window_options; +use crate::notifications::collab_notification::CollabNotification; use call::{ActiveCall, IncomingCall}; use futures::StreamExt; -use gpui::{ - img, px, AppContext, ParentElement, Render, RenderOnce, Styled, ViewContext, - VisualContext as _, WindowHandle, -}; +use gpui::{prelude::*, AppContext, WindowHandle}; use settings::Settings; use std::sync::{Arc, Weak}; use theme::ThemeSettings; -use ui::prelude::*; -use ui::{h_stack, v_stack, Button, Label}; +use ui::{prelude::*, Button, Label}; use util::ResultExt; use workspace::AppState; @@ -31,8 +28,8 @@ pub fn init(app_state: &Arc, cx: &mut AppContext) { if let Some(incoming_call) = incoming_call { let unique_screens = cx.update(|cx| cx.displays()).unwrap(); let window_size = gpui::Size { - width: px(380.), - height: px(64.), + width: px(400.), + height: px(72.), }; for screen in unique_screens { @@ -129,35 +126,22 @@ impl Render for IncomingCallNotification { cx.set_rem_size(ui_font_size); - h_stack() - .font(ui_font) - .text_ui() - .justify_between() - .size_full() - .overflow_hidden() - .elevation_3(cx) - .p_2() - .gap_2() - .child( - img(self.state.call.calling_user.avatar_uri.clone()) - .w_12() - .h_12() - .rounded_full(), + div().size_full().font(ui_font).child( + CollabNotification::new( + self.state.call.calling_user.avatar_uri.clone(), + Button::new("accept", "Accept").on_click({ + let state = self.state.clone(); + move |_, cx| state.respond(true, cx) + }), + Button::new("decline", "Decline").on_click({ + let state = self.state.clone(); + move |_, cx| state.respond(false, cx) + }), ) .child(v_stack().overflow_hidden().child(Label::new(format!( "{} is sharing a project in Zed", self.state.call.calling_user.github_login - )))) - .child( - v_stack() - .child(Button::new("accept", "Accept").render(cx).on_click({ - let state = self.state.clone(); - move |_, cx| state.respond(true, cx) - })) - .child(Button::new("decline", "Decline").render(cx).on_click({ - let state = self.state.clone(); - move |_, cx| state.respond(false, cx) - })), - ) + )))), + ) } } diff --git a/crates/collab_ui/src/notifications/project_shared_notification.rs b/crates/collab_ui/src/notifications/project_shared_notification.rs index 982214c3e596e7290a4837264cc570c5aa786c94..79adc69a801dbe5ed30c7b9afb8d1df19bba6c5e 100644 --- a/crates/collab_ui/src/notifications/project_shared_notification.rs +++ b/crates/collab_ui/src/notifications/project_shared_notification.rs @@ -1,12 +1,13 @@ use crate::notification_window_options; +use crate::notifications::collab_notification::CollabNotification; use call::{room, ActiveCall}; use client::User; use collections::HashMap; -use gpui::{img, px, AppContext, ParentElement, Render, Size, Styled, ViewContext, VisualContext}; +use gpui::{AppContext, Size}; use settings::Settings; use std::sync::{Arc, Weak}; use theme::ThemeSettings; -use ui::{h_stack, prelude::*, v_stack, Button, Label}; +use ui::{prelude::*, Button, Label}; use workspace::AppState; pub fn init(app_state: &Arc, cx: &mut AppContext) { @@ -130,51 +131,30 @@ impl Render for ProjectSharedNotification { cx.set_rem_size(ui_font_size); - h_stack() - .font(ui_font) - .text_ui() - .justify_between() - .size_full() - .overflow_hidden() - .elevation_3(cx) - .p_2() - .gap_2() - .child( - img(self.owner.avatar_uri.clone()) - .w_12() - .h_12() - .rounded_full(), - ) - .child( - v_stack() - .overflow_hidden() - .child(Label::new(self.owner.github_login.clone())) - .child(Label::new(format!( - "is sharing a project in Zed{}", - if self.worktree_root_names.is_empty() { - "" - } else { - ":" - } - ))) - .children(if self.worktree_root_names.is_empty() { - None - } else { - Some(Label::new(self.worktree_root_names.join(", "))) - }), - ) - .child( - v_stack() - .child(Button::new("open", "Open").on_click(cx.listener( - move |this, _event, cx| { - this.join(cx); - }, - ))) - .child(Button::new("dismiss", "Dismiss").on_click(cx.listener( - move |this, _event, cx| { - this.dismiss(cx); - }, - ))), + div().size_full().font(ui_font).child( + CollabNotification::new( + self.owner.avatar_uri.clone(), + Button::new("open", "Open").on_click(cx.listener(move |this, _event, cx| { + this.join(cx); + })), + Button::new("dismiss", "Dismiss").on_click(cx.listener(move |this, _event, cx| { + this.dismiss(cx); + })), ) + .child(Label::new(self.owner.github_login.clone())) + .child(Label::new(format!( + "is sharing a project in Zed{}", + if self.worktree_root_names.is_empty() { + "" + } else { + ":" + } + ))) + .children(if self.worktree_root_names.is_empty() { + None + } else { + Some(Label::new(self.worktree_root_names.join(", "))) + }), + ) } } diff --git a/crates/collab_ui/src/notifications/stories.rs b/crates/collab_ui/src/notifications/stories.rs new file mode 100644 index 0000000000000000000000000000000000000000..36518679c661627346e45357161ba117a6bfe5b3 --- /dev/null +++ b/crates/collab_ui/src/notifications/stories.rs @@ -0,0 +1,3 @@ +mod collab_notification; + +pub use collab_notification::*; diff --git a/crates/collab_ui/src/notifications/stories/collab_notification.rs b/crates/collab_ui/src/notifications/stories/collab_notification.rs new file mode 100644 index 0000000000000000000000000000000000000000..c43cac46d21352ac8375e33cf530891385c36da0 --- /dev/null +++ b/crates/collab_ui/src/notifications/stories/collab_notification.rs @@ -0,0 +1,50 @@ +use gpui::prelude::*; +use story::{StoryContainer, StoryItem, StorySection}; +use ui::prelude::*; + +use crate::notifications::collab_notification::CollabNotification; + +pub struct CollabNotificationStory; + +impl Render for CollabNotificationStory { + fn render(&mut self, _cx: &mut ViewContext) -> impl IntoElement { + let window_container = |width, height| div().w(px(width)).h(px(height)); + + StoryContainer::new( + "CollabNotification Story", + "crates/collab_ui/src/notifications/stories/collab_notification.rs", + ) + .child( + StorySection::new().child(StoryItem::new( + "Incoming Call Notification", + window_container(400., 72.).child( + CollabNotification::new( + "https://avatars.githubusercontent.com/u/1486634?v=4", + Button::new("accept", "Accept"), + Button::new("decline", "Decline"), + ) + .child( + v_stack() + .overflow_hidden() + .child(Label::new("maxdeviant is sharing a project in Zed")), + ), + ), + )), + ) + .child( + StorySection::new().child(StoryItem::new( + "Project Shared Notification", + window_container(400., 72.).child( + CollabNotification::new( + "https://avatars.githubusercontent.com/u/1714999?v=4", + Button::new("open", "Open"), + Button::new("dismiss", "Dismiss"), + ) + .child(Label::new("iamnbutler")) + .child(Label::new("is sharing a project in Zed:")) + .child(Label::new("zed")), + ), + )), + ) + } +} diff --git a/crates/storybook/Cargo.toml b/crates/storybook/Cargo.toml index 033b3fa8d9077ee9866f0a0a7e382d5f68d3e483..9f08556757b6b59d1aed6fa7361029a36664a40a 100644 --- a/crates/storybook/Cargo.toml +++ b/crates/storybook/Cargo.toml @@ -14,6 +14,7 @@ anyhow.workspace = true backtrace-on-stack-overflow = "0.3.0" chrono = "0.4" clap = { version = "4.4", features = ["derive", "string"] } +collab_ui = { path = "../collab_ui", features = ["stories"] } strum = { version = "0.25.0", features = ["derive"] } dialoguer = { version = "0.11.0", features = ["fuzzy-select"] } editor = { path = "../editor" } diff --git a/crates/storybook/src/story_selector.rs b/crates/storybook/src/story_selector.rs index 27ddfe26ac2884cd327f065c01bdf8bf7617e624..120e60d34a1499ed143394713e0c46fbf1bc2dfa 100644 --- a/crates/storybook/src/story_selector.rs +++ b/crates/storybook/src/story_selector.rs @@ -16,6 +16,7 @@ pub enum ComponentStory { Avatar, Button, Checkbox, + CollabNotification, ContextMenu, Cursor, Disclosure, @@ -45,6 +46,9 @@ impl ComponentStory { Self::Avatar => cx.new_view(|_| ui::AvatarStory).into(), Self::Button => cx.new_view(|_| ui::ButtonStory).into(), Self::Checkbox => cx.new_view(|_| ui::CheckboxStory).into(), + Self::CollabNotification => cx + .new_view(|_| collab_ui::notifications::CollabNotificationStory) + .into(), Self::ContextMenu => cx.new_view(|_| ui::ContextMenuStory).into(), Self::Cursor => cx.new_view(|_| crate::stories::CursorStory).into(), Self::Disclosure => cx.new_view(|_| ui::DisclosureStory).into(),