diff --git a/crates/theme/src/colors.rs b/crates/theme/src/colors.rs index f2ca4bd0..7d82b037 100644 --- a/crates/theme/src/colors.rs +++ b/crates/theme/src/colors.rs @@ -127,7 +127,7 @@ impl ThemeColors { text: neutral().light().step_12(), text_muted: neutral().light().step_11(), text_placeholder: neutral().light().step_10(), - text_accent: brand().light().step_9(), + text_accent: brand().light().step_11(), text_danger: danger().light().step_9(), text_warning: warning().light().step_9(), @@ -213,7 +213,7 @@ impl ThemeColors { text: neutral().dark().step_12(), text_muted: neutral().dark().step_11(), text_placeholder: neutral().dark().step_10(), - text_accent: brand().dark().step_9(), + text_accent: brand().dark().step_11(), text_danger: danger().dark().step_9(), text_warning: warning().dark().step_9(), diff --git a/crates/ui/src/dialog.rs b/crates/ui/src/dialog.rs index 16eeff21..3e4a275b 100644 --- a/crates/ui/src/dialog.rs +++ b/crates/ui/src/dialog.rs @@ -3,16 +3,17 @@ use std::rc::Rc; use gpui::prelude::FluentBuilder; use gpui::{ Animation, AnimationExt as _, AnyElement, App, BoxShadow, ClickEvent, Div, FocusHandle, - InteractiveElement as _, IntoElement, ParentElement, Pixels, RenderOnce, SharedString, - StyleRefinement, Styled, Window, div, hsla, point, px, size, + IntoElement, ParentElement, Pixels, RenderOnce, SharedString, StyleRefinement, Styled, Window, + div, hsla, point, px, }; +use gpui_base::{DialogBackdrop, DialogPopup, DialogTitle}; use instant::Duration; use theme::ActiveTheme; use crate::animation::cubic_bezier; use crate::button::{Button, ButtonCustomVariant, ButtonVariant, ButtonVariants as _}; use crate::scroll::ScrollableElement; -use crate::{IconName, Root, Sizable, StyledExt, WindowExtension, h_flex, v_flex}; +use crate::{IconName, Root, StyledExt, WindowExtension, h_flex, v_flex}; type OnClose = Rc; type OnOk = Option bool + 'static>>; @@ -68,7 +69,6 @@ pub struct Dialog { content: Div, width: Pixels, max_width: Option, - margin_top: Option, on_close: OnClose, on_ok: OnOk, @@ -94,7 +94,6 @@ impl Dialog { title: None, footer: None, content: v_flex(), - margin_top: None, width: px(380.), max_width: None, overlay: true, @@ -232,6 +231,7 @@ impl RenderOnce for Dialog { let layer_ix = self.layer_ix; let is_topmost = layer_ix + 1 == Root::read(window, cx).active_dialogs.len(); let has_footer = self.footer.is_some(); + let on_close = self.on_close.clone(); let on_ok = self.on_ok.clone(); let on_cancel = self.on_cancel.clone(); @@ -246,8 +246,7 @@ impl RenderOnce for Dialog { Button::new("ok") .label(ok_text) .with_variant(ok_variant) - .small() - .flex_1() + .font_semibold() .on_click({ let on_ok = on_ok.clone(); let on_close = on_close.clone(); @@ -280,8 +279,6 @@ impl RenderOnce for Dialog { Button::new("cancel") .label(cancel_text) .with_variant(cancel_variant) - .small() - .flex_1() .on_click({ let on_cancel = on_cancel.clone(); let on_close = on_close.clone(); @@ -298,18 +295,6 @@ impl RenderOnce for Dialog { } }); - let window_paddings = crate::root::window_paddings(window, cx); - let radius = cx.theme().radius_lg; - - let view_size = window.viewport_size() - - size( - window_paddings.left + window_paddings.right, - window_paddings.top + window_paddings.bottom, - ); - - let offset_top = px(layer_ix as f32 * 16.); - let top_offset = self.margin_top.unwrap_or(px(0.)) + offset_top; - let mut padding_right = px(16.); let mut padding_left = px(16.); @@ -324,32 +309,25 @@ impl RenderOnce for Dialog { let animation = Animation::new(Duration::from_secs_f64(0.25)) .with_easing(cubic_bezier(0.32, 0.72, 0., 1.)); - let backdrop = div() + let backdrop = DialogBackdrop::new() .absolute() - .top(window_paddings.top) - .left(window_paddings.left) - .w(view_size.width) - .h(view_size.height) - .when(self.overlay_visible, |this| { - this.occlude().bg(cx.theme().overlay) - }) + .inset_0() + .when(self.overlay_visible, |this| this.bg(cx.theme().overlay)) .with_animation("fade-in", animation.clone(), move |this, delta| { this.opacity(delta) }); - let card = v_flex() - .id(layer_ix) + let card = DialogPopup::new() + .flex() + .flex_col() .bg(cx.theme().background) .border_1() .border_color(cx.theme().border.alpha(0.4)) - .rounded(radius) + .rounded(cx.theme().radius_lg) .when(cx.theme().shadow, |this| this.shadow_xl()) .min_h_24() .refine_style(&self.style) - // There style is high priority, can't be overridden. - .occlude() .relative() - .top(top_offset) .w(self.width) .when_some(self.max_width, |this, w| this.max_w(w)) .child( @@ -362,7 +340,12 @@ impl RenderOnce for Dialog { .justify_center() .relative() .when_some(self.title, |this, title| { - this.h_10().font_semibold().text_center().child(title) + this.h_10().child( + DialogTitle::new() + .font_semibold() + .text_center() + .child(title), + ) }) .when(self.show_close, |this| { let on_cancel = on_cancel.clone(); @@ -415,7 +398,7 @@ impl RenderOnce for Dialog { .when_some(self.footer, |this, footer| { this.child( h_flex() - .gap_2() + .gap_1() .pt(padding_left) .pr(padding_right) .pb(padding_left) @@ -444,11 +427,13 @@ impl RenderOnce for Dialog { inset: false, }, ]; - this.top(top_offset + y_offset).shadow(shadow) + this.top(y_offset).shadow(shadow) }); gpui_base::Dialog::new(cx) .layer(layer_ix, is_topmost) + .backdrop(backdrop) + .popup(card) .focus_handle(self.focus_handle.clone()) .close_on_escape(self.keyboard) .close_on_backdrop_press(self.overlay_closable) @@ -461,7 +446,5 @@ impl RenderOnce for Dialog { on_close(event, window, cx); window.close_dialog(cx); }) - .backdrop(backdrop) - .popup(card) } } diff --git a/crates/ui/src/root.rs b/crates/ui/src/root.rs index 596ed318..eb937941 100644 --- a/crates/ui/src/root.rs +++ b/crates/ui/src/root.rs @@ -3,7 +3,7 @@ use std::rc::Rc; use gpui::prelude::FluentBuilder; use gpui::{ - AnyView, App, AppContext, Bounds, Context, CursorStyle, Decorations, Edges, ElementId, Entity, + AnyView, App, AppContext, Bounds, Context, CursorStyle, Decorations, ElementId, Entity, FocusHandle, HitboxBehavior, Hsla, InteractiveElement, IntoElement, MouseButton, ParentElement as _, Pixels, Point, Render, ResizeEdge, Size, Styled, Tiling, WeakFocusHandle, Window, canvas, div, point, px, size, @@ -389,29 +389,6 @@ impl Render for Root { } } -/// Get the window paddings. -pub(crate) fn window_paddings(window: &Window, _cx: &App) -> Edges { - match window.window_decorations() { - Decorations::Server => Edges::all(px(0.0)), - Decorations::Client { tiling } => { - let mut paddings = Edges::all(CLIENT_SIDE_DECORATION_SHADOW); - if tiling.top { - paddings.top = px(0.0); - } - if tiling.bottom { - paddings.bottom = px(0.0); - } - if tiling.left { - paddings.left = px(0.0); - } - if tiling.right { - paddings.right = px(0.0); - } - paddings - } - } -} - /// Get the window resize edge. fn resize_edge( pos: Point, diff --git a/crates/workspace/src/dialogs/import.rs b/crates/workspace/src/dialogs/import.rs index 20a0963c..6a1cba78 100644 --- a/crates/workspace/src/dialogs/import.rs +++ b/crates/workspace/src/dialogs/import.rs @@ -10,7 +10,7 @@ use state::{CoopAuthUrlHandler, NostrRegistry, USER_KEYRING}; use theme::ActiveTheme; use ui::button::{Button, ButtonVariants}; use ui::input::{Input, InputEvent, InputState}; -use ui::{Disableable, StyledExt, WindowExtension, divider, v_flex}; +use ui::{Disableable, StyledExt, WindowExtension, v_flex}; pub fn open(window: &mut Window, cx: &mut App) { let import = cx.new(|cx| ImportIdentity::new(window, cx)); @@ -20,7 +20,7 @@ pub fn open(window: &mut Window, cx: &mut App) { .show_close(false) .overlay_closable(false) .keyboard(false) - .title("Get Started") + .title("Continue with your Nostr Identity") .child(import.clone()) }); } @@ -29,20 +29,13 @@ pub fn open(window: &mut Window, cx: &mut App) { pub struct ImportIdentity { /// Secret key input key_input: Entity, - /// Password input (if required) pass_input: Entity, - /// Error message error: Entity>, - /// Whether the user is currently loading loading: bool, - - /// Async tasks tasks: Vec>>, - - /// Input subscription _subscription: Option, } @@ -230,7 +223,7 @@ impl Render for ImportIdentity { v_flex() .size_full() - .gap_4() + .gap_2() .text_sm() .child( v_flex() @@ -279,12 +272,11 @@ impl Render for ImportIdentity { this.login(window, cx); })), ) - .child(divider(cx)) .when(!is_wasm, |this| { this.child( Button::new("proxy") .label("Connect via Web Extension (Experimental)") - .ghost_alt() + .ghost() .loading(self.loading) .disabled(self.loading) .on_click(cx.listener(move |this, _ev, _window, cx| { diff --git a/crates/workspace/src/dialogs/join_group.rs b/crates/workspace/src/dialogs/join_group.rs index 07c58d06..418bd6bf 100644 --- a/crates/workspace/src/dialogs/join_group.rs +++ b/crates/workspace/src/dialogs/join_group.rs @@ -1,35 +1,75 @@ -use gpui::{App, AppContext, ParentElement, Styled, Window, px}; +use std::rc::Rc; + +use gpui::{App, AppContext, ClickEvent, ParentElement, Styled, Window, px}; use nip29::{GroupReference, GroupsRegistry}; -use ui::WindowExtension; +use ui::button::{Button, ButtonVariants}; use ui::input::{Input, InputState}; use ui::notification::Notification; +use ui::{IconName, StyledExt, WindowExtension, h_flex, v_flex}; + +type SubmitFn = Rc bool>; pub fn open(window: &mut Window, cx: &mut App) { let input = cx.new(|cx| InputState::new(window, cx).placeholder("naddr1…")); - window.open_dialog(cx, move |this, _window, _cx| { + let join: SubmitFn = { let input = input.clone(); + Rc::new(move |_, window, cx| { + let groups = GroupsRegistry::global(cx); + let reference = match GroupReference::parse(input.read(cx).value().trim()) { + Ok(reference) => reference, + Err(error) => { + window.push_notification(Notification::error(error.to_string()), cx); + return false; + } + }; + + groups.update(cx, |registry, cx| { + registry.join(reference, cx); + }); + + true + }) + }; + + window.open_dialog(cx, move |this, _window, _cx| { this.width(px(380.)) - .confirm() .title("Join Group") - .child(Input::new(&input).cleanable(true).flex_1().text_sm()) - .on_ok(move |_event, window, cx| { - let groups = GroupsRegistry::global(cx); - - let reference = match GroupReference::parse(input.read(cx).value().trim()) { - Ok(reference) => reference, - Err(error) => { - window.push_notification(Notification::error(error.to_string()), cx); - return false; - } - }; - - groups.update(cx, |registry, cx| { - registry.join(reference, cx); - }); - - true + .on_ok({ + let join = join.clone(); + move |event, window, cx| join(event, window, cx) }) + .child( + v_flex() + .gap_3() + .child(Input::new(&input).cleanable(true).text_sm()) + .child( + h_flex() + .gap_1() + .justify_end() + .child( + Button::new("cancel") + .label("Cancel") + .ghost() + .on_click(|_, window, cx| window.close_dialog(cx)), + ) + .child( + Button::new("join") + .icon(IconName::CheckCircle) + .label("Join") + .primary() + .font_semibold() + .on_click({ + let join = join.clone(); + move |event, window, cx| { + if join(event, window, cx) { + window.close_dialog(cx); + } + } + }), + ), + ), + ) }); } diff --git a/crates/workspace/src/dialogs/new_chat.rs b/crates/workspace/src/dialogs/new_chat.rs index 8ff55b80..9a7bc2a7 100644 --- a/crates/workspace/src/dialogs/new_chat.rs +++ b/crates/workspace/src/dialogs/new_chat.rs @@ -16,11 +16,10 @@ use state::NostrRegistry; use theme::ActiveTheme; use ui::avatar::Avatar; use ui::button::{Button, ButtonVariants}; -use ui::dialog::DialogButtonProps; use ui::dock::{DockArea, DockPlacement, PanelHandle}; use ui::input::{Input, InputEvent, InputState}; use ui::nav::Nav; -use ui::{Disableable, IconName, Sizable, WindowExtension, h_flex, v_flex}; +use ui::{Disableable, IconName, Sizable, StyledExt, WindowExtension, h_flex, v_flex}; /// Maximum number of members a chat can have. const MAX_MEMBERS: usize = 3; @@ -30,21 +29,7 @@ pub fn open(dock: WeakEntity, window: &mut Window, cx: &mut App) { window.open_dialog(cx, { let view = view.clone(); - move |this, _window, _cx| { - this.width(px(420.)) - .title("New chat") - .confirm() - .button_props( - DialogButtonProps::default() - .cancel_text("Cancel") - .ok_text("Start"), - ) - .on_ok({ - let view = view.clone(); - move |_, window, cx| view.update(cx, |this, cx| this.start_chat(window, cx)) - }) - .child(view.clone()) - } + move |this, _window, _cx| this.width(px(420.)).title("New chat").child(view.clone()) }); } @@ -452,5 +437,28 @@ impl Render for NewChat { ) .h_80(), ) + .child( + h_flex() + .gap_1() + .justify_end() + .child( + Button::new("cancel") + .label("Cancel") + .ghost() + .on_click(|_, window, cx| window.close_dialog(cx)), + ) + .child( + Button::new("start") + .icon(IconName::CheckCircle) + .label("Start") + .primary() + .font_semibold() + .on_click(cx.listener(move |this, _event, window, cx| { + if this.start_chat(window, cx) { + window.close_dialog(cx); + } + })), + ), + ) } } diff --git a/crates/workspace/src/dialogs/new_group.rs b/crates/workspace/src/dialogs/new_group.rs index 13499a7d..1a4b9c5c 100644 --- a/crates/workspace/src/dialogs/new_group.rs +++ b/crates/workspace/src/dialogs/new_group.rs @@ -1,24 +1,33 @@ -use gpui::{App, AppContext, Div, Entity, ParentElement, Styled, Window, px}; +use std::rc::Rc; + +use gpui::{App, AppContext, ClickEvent, Div, Entity, ParentElement, Styled, Window, px}; use nip29::{GroupId, GroupKey, GroupMetadata, GroupsRegistry}; use nostr_sdk::prelude::*; use theme::ActiveTheme; +use ui::button::{Button, ButtonVariants}; use ui::input::{Input, InputState}; use ui::notification::Notification; -use ui::{WindowExtension, v_flex}; +use ui::{IconName, StyledExt, WindowExtension, h_flex, v_flex}; + +type SubmitFn = Rc bool>; pub fn open(window: &mut Window, cx: &mut App) { let name = cx.new(|cx| InputState::new(window, cx).placeholder("Group name")); let id = cx.new(|cx| InputState::new(window, cx).placeholder("Group id")); let relay = cx.new(|cx| InputState::new(window, cx).placeholder("wss://relay.example.com")); - window.open_dialog(cx, move |this, _window, cx| { - let name = name.clone(); - let id = id.clone(); - let relay = relay.clone(); + let submit: SubmitFn = { + let (name, id, relay) = (name.clone(), id.clone(), relay.clone()); + Rc::new(move |_, window, cx| create(&name, &id, &relay, window, cx)) + }; + window.open_dialog(cx, move |this, _window, cx| { this.width(px(380.)) - .confirm() .title("New group") + .on_ok({ + let submit = submit.clone(); + move |event, window, cx| submit(event, window, cx) + }) .child( v_flex() .gap_3() @@ -26,7 +35,32 @@ pub fn open(window: &mut Window, cx: &mut App) { .child(field("Group id", &id, cx)) .child(field("Relay", &relay, cx)), ) - .on_ok(move |_event, window, cx| create(&name, &id, &relay, window, cx)) + .child( + h_flex() + .gap_1() + .justify_end() + .child( + Button::new("cancel") + .label("Cancel") + .ghost() + .on_click(|_, window, cx| window.close_dialog(cx)), + ) + .child( + Button::new("create") + .icon(IconName::CheckCircle) + .label("Create") + .primary() + .font_semibold() + .on_click({ + let submit = submit.clone(); + move |event, window, cx| { + if submit(event, window, cx) { + window.close_dialog(cx); + } + } + }), + ), + ) }); } diff --git a/crates/workspace/src/dialogs/restore.rs b/crates/workspace/src/dialogs/restore.rs index 5511c909..eeedc759 100644 --- a/crates/workspace/src/dialogs/restore.rs +++ b/crates/workspace/src/dialogs/restore.rs @@ -10,7 +10,7 @@ use nostr_sdk::prelude::*; use theme::ActiveTheme; use ui::button::{Button, ButtonVariants}; use ui::input::{Input, InputEvent, InputState}; -use ui::{WindowExtension, v_flex}; +use ui::{IconName, StyledExt, WindowExtension, h_flex, v_flex}; #[derive(Debug)] pub struct RestoreEncryption { @@ -99,7 +99,7 @@ impl Render for RestoreEncryption { fn render(&mut self, _window: &mut gpui::Window, cx: &mut Context) -> impl IntoElement { v_flex() .size_full() - .gap_2() + .gap_3() .text_sm() .child( v_flex() @@ -110,12 +110,16 @@ impl Render for RestoreEncryption { .child(Input::new(&self.key_input)), ) .child( - Button::new("restore") - .label("Restore") - .primary() - .on_click(cx.listener(move |this, _, window, cx| { - this.restore(window, cx); - })), + h_flex().gap_1().justify_end().child( + Button::new("restore") + .icon(IconName::CheckCircle) + .label("Restore") + .primary() + .font_semibold() + .on_click(cx.listener(move |this, _, window, cx| { + this.restore(window, cx); + })), + ), ) .when_some(self.error.read(cx).as_ref(), |this, error| { this.child( diff --git a/crates/workspace/src/dialogs/screening.rs b/crates/workspace/src/dialogs/screening.rs index 8a61cfd6..787e822f 100644 --- a/crates/workspace/src/dialogs/screening.rs +++ b/crates/workspace/src/dialogs/screening.rs @@ -22,27 +22,18 @@ pub fn init(public_key: PublicKey, window: &mut Window, cx: &mut App) -> Entity< cx.new(|cx| Screening::new(public_key, window, cx)) } -/// Screening pub struct Screening { /// Public Key of the person being screened. public_key: PublicKey, - /// Whether the person's address is verified. verified: bool, - /// Whether the person is followed by current user. followed: bool, - /// Last time the person was active. last_active: Option, - /// All mutual contacts of the person being screened. mutual_contacts: Vec, - - /// Async tasks tasks: SmallVec<[Task<()>; 3]>, - - /// Subscriptions _subscriptions: SmallVec<[Subscription; 1]>, }