chore: refactor dialogs to render custom footer buttons
This commit is contained in:
@@ -127,7 +127,7 @@ impl ThemeColors {
|
|||||||
text: neutral().light().step_12(),
|
text: neutral().light().step_12(),
|
||||||
text_muted: neutral().light().step_11(),
|
text_muted: neutral().light().step_11(),
|
||||||
text_placeholder: neutral().light().step_10(),
|
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_danger: danger().light().step_9(),
|
||||||
text_warning: warning().light().step_9(),
|
text_warning: warning().light().step_9(),
|
||||||
|
|
||||||
@@ -213,7 +213,7 @@ impl ThemeColors {
|
|||||||
text: neutral().dark().step_12(),
|
text: neutral().dark().step_12(),
|
||||||
text_muted: neutral().dark().step_11(),
|
text_muted: neutral().dark().step_11(),
|
||||||
text_placeholder: neutral().dark().step_10(),
|
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_danger: danger().dark().step_9(),
|
||||||
text_warning: warning().dark().step_9(),
|
text_warning: warning().dark().step_9(),
|
||||||
|
|
||||||
|
|||||||
+23
-40
@@ -3,16 +3,17 @@ use std::rc::Rc;
|
|||||||
use gpui::prelude::FluentBuilder;
|
use gpui::prelude::FluentBuilder;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
Animation, AnimationExt as _, AnyElement, App, BoxShadow, ClickEvent, Div, FocusHandle,
|
Animation, AnimationExt as _, AnyElement, App, BoxShadow, ClickEvent, Div, FocusHandle,
|
||||||
InteractiveElement as _, IntoElement, ParentElement, Pixels, RenderOnce, SharedString,
|
IntoElement, ParentElement, Pixels, RenderOnce, SharedString, StyleRefinement, Styled, Window,
|
||||||
StyleRefinement, Styled, Window, div, hsla, point, px, size,
|
div, hsla, point, px,
|
||||||
};
|
};
|
||||||
|
use gpui_base::{DialogBackdrop, DialogPopup, DialogTitle};
|
||||||
use instant::Duration;
|
use instant::Duration;
|
||||||
use theme::ActiveTheme;
|
use theme::ActiveTheme;
|
||||||
|
|
||||||
use crate::animation::cubic_bezier;
|
use crate::animation::cubic_bezier;
|
||||||
use crate::button::{Button, ButtonCustomVariant, ButtonVariant, ButtonVariants as _};
|
use crate::button::{Button, ButtonCustomVariant, ButtonVariant, ButtonVariants as _};
|
||||||
use crate::scroll::ScrollableElement;
|
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<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>;
|
type OnClose = Rc<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>;
|
||||||
type OnOk = Option<Rc<dyn Fn(&ClickEvent, &mut Window, &mut App) -> bool + 'static>>;
|
type OnOk = Option<Rc<dyn Fn(&ClickEvent, &mut Window, &mut App) -> bool + 'static>>;
|
||||||
@@ -68,7 +69,6 @@ pub struct Dialog {
|
|||||||
content: Div,
|
content: Div,
|
||||||
width: Pixels,
|
width: Pixels,
|
||||||
max_width: Option<Pixels>,
|
max_width: Option<Pixels>,
|
||||||
margin_top: Option<Pixels>,
|
|
||||||
|
|
||||||
on_close: OnClose,
|
on_close: OnClose,
|
||||||
on_ok: OnOk,
|
on_ok: OnOk,
|
||||||
@@ -94,7 +94,6 @@ impl Dialog {
|
|||||||
title: None,
|
title: None,
|
||||||
footer: None,
|
footer: None,
|
||||||
content: v_flex(),
|
content: v_flex(),
|
||||||
margin_top: None,
|
|
||||||
width: px(380.),
|
width: px(380.),
|
||||||
max_width: None,
|
max_width: None,
|
||||||
overlay: true,
|
overlay: true,
|
||||||
@@ -232,6 +231,7 @@ impl RenderOnce for Dialog {
|
|||||||
let layer_ix = self.layer_ix;
|
let layer_ix = self.layer_ix;
|
||||||
let is_topmost = layer_ix + 1 == Root::read(window, cx).active_dialogs.len();
|
let is_topmost = layer_ix + 1 == Root::read(window, cx).active_dialogs.len();
|
||||||
let has_footer = self.footer.is_some();
|
let has_footer = self.footer.is_some();
|
||||||
|
|
||||||
let on_close = self.on_close.clone();
|
let on_close = self.on_close.clone();
|
||||||
let on_ok = self.on_ok.clone();
|
let on_ok = self.on_ok.clone();
|
||||||
let on_cancel = self.on_cancel.clone();
|
let on_cancel = self.on_cancel.clone();
|
||||||
@@ -246,8 +246,7 @@ impl RenderOnce for Dialog {
|
|||||||
Button::new("ok")
|
Button::new("ok")
|
||||||
.label(ok_text)
|
.label(ok_text)
|
||||||
.with_variant(ok_variant)
|
.with_variant(ok_variant)
|
||||||
.small()
|
.font_semibold()
|
||||||
.flex_1()
|
|
||||||
.on_click({
|
.on_click({
|
||||||
let on_ok = on_ok.clone();
|
let on_ok = on_ok.clone();
|
||||||
let on_close = on_close.clone();
|
let on_close = on_close.clone();
|
||||||
@@ -280,8 +279,6 @@ impl RenderOnce for Dialog {
|
|||||||
Button::new("cancel")
|
Button::new("cancel")
|
||||||
.label(cancel_text)
|
.label(cancel_text)
|
||||||
.with_variant(cancel_variant)
|
.with_variant(cancel_variant)
|
||||||
.small()
|
|
||||||
.flex_1()
|
|
||||||
.on_click({
|
.on_click({
|
||||||
let on_cancel = on_cancel.clone();
|
let on_cancel = on_cancel.clone();
|
||||||
let on_close = on_close.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_right = px(16.);
|
||||||
let mut padding_left = 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))
|
let animation = Animation::new(Duration::from_secs_f64(0.25))
|
||||||
.with_easing(cubic_bezier(0.32, 0.72, 0., 1.));
|
.with_easing(cubic_bezier(0.32, 0.72, 0., 1.));
|
||||||
|
|
||||||
let backdrop = div()
|
let backdrop = DialogBackdrop::new()
|
||||||
.absolute()
|
.absolute()
|
||||||
.top(window_paddings.top)
|
.inset_0()
|
||||||
.left(window_paddings.left)
|
.when(self.overlay_visible, |this| this.bg(cx.theme().overlay))
|
||||||
.w(view_size.width)
|
|
||||||
.h(view_size.height)
|
|
||||||
.when(self.overlay_visible, |this| {
|
|
||||||
this.occlude().bg(cx.theme().overlay)
|
|
||||||
})
|
|
||||||
.with_animation("fade-in", animation.clone(), move |this, delta| {
|
.with_animation("fade-in", animation.clone(), move |this, delta| {
|
||||||
this.opacity(delta)
|
this.opacity(delta)
|
||||||
});
|
});
|
||||||
|
|
||||||
let card = v_flex()
|
let card = DialogPopup::new()
|
||||||
.id(layer_ix)
|
.flex()
|
||||||
|
.flex_col()
|
||||||
.bg(cx.theme().background)
|
.bg(cx.theme().background)
|
||||||
.border_1()
|
.border_1()
|
||||||
.border_color(cx.theme().border.alpha(0.4))
|
.border_color(cx.theme().border.alpha(0.4))
|
||||||
.rounded(radius)
|
.rounded(cx.theme().radius_lg)
|
||||||
.when(cx.theme().shadow, |this| this.shadow_xl())
|
.when(cx.theme().shadow, |this| this.shadow_xl())
|
||||||
.min_h_24()
|
.min_h_24()
|
||||||
.refine_style(&self.style)
|
.refine_style(&self.style)
|
||||||
// There style is high priority, can't be overridden.
|
|
||||||
.occlude()
|
|
||||||
.relative()
|
.relative()
|
||||||
.top(top_offset)
|
|
||||||
.w(self.width)
|
.w(self.width)
|
||||||
.when_some(self.max_width, |this, w| this.max_w(w))
|
.when_some(self.max_width, |this, w| this.max_w(w))
|
||||||
.child(
|
.child(
|
||||||
@@ -362,7 +340,12 @@ impl RenderOnce for Dialog {
|
|||||||
.justify_center()
|
.justify_center()
|
||||||
.relative()
|
.relative()
|
||||||
.when_some(self.title, |this, title| {
|
.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| {
|
.when(self.show_close, |this| {
|
||||||
let on_cancel = on_cancel.clone();
|
let on_cancel = on_cancel.clone();
|
||||||
@@ -415,7 +398,7 @@ impl RenderOnce for Dialog {
|
|||||||
.when_some(self.footer, |this, footer| {
|
.when_some(self.footer, |this, footer| {
|
||||||
this.child(
|
this.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
.gap_2()
|
.gap_1()
|
||||||
.pt(padding_left)
|
.pt(padding_left)
|
||||||
.pr(padding_right)
|
.pr(padding_right)
|
||||||
.pb(padding_left)
|
.pb(padding_left)
|
||||||
@@ -444,11 +427,13 @@ impl RenderOnce for Dialog {
|
|||||||
inset: false,
|
inset: false,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
this.top(top_offset + y_offset).shadow(shadow)
|
this.top(y_offset).shadow(shadow)
|
||||||
});
|
});
|
||||||
|
|
||||||
gpui_base::Dialog::new(cx)
|
gpui_base::Dialog::new(cx)
|
||||||
.layer(layer_ix, is_topmost)
|
.layer(layer_ix, is_topmost)
|
||||||
|
.backdrop(backdrop)
|
||||||
|
.popup(card)
|
||||||
.focus_handle(self.focus_handle.clone())
|
.focus_handle(self.focus_handle.clone())
|
||||||
.close_on_escape(self.keyboard)
|
.close_on_escape(self.keyboard)
|
||||||
.close_on_backdrop_press(self.overlay_closable)
|
.close_on_backdrop_press(self.overlay_closable)
|
||||||
@@ -461,7 +446,5 @@ impl RenderOnce for Dialog {
|
|||||||
on_close(event, window, cx);
|
on_close(event, window, cx);
|
||||||
window.close_dialog(cx);
|
window.close_dialog(cx);
|
||||||
})
|
})
|
||||||
.backdrop(backdrop)
|
|
||||||
.popup(card)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-24
@@ -3,7 +3,7 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
use gpui::prelude::FluentBuilder;
|
use gpui::prelude::FluentBuilder;
|
||||||
use gpui::{
|
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,
|
FocusHandle, HitboxBehavior, Hsla, InteractiveElement, IntoElement, MouseButton,
|
||||||
ParentElement as _, Pixels, Point, Render, ResizeEdge, Size, Styled, Tiling, WeakFocusHandle,
|
ParentElement as _, Pixels, Point, Render, ResizeEdge, Size, Styled, Tiling, WeakFocusHandle,
|
||||||
Window, canvas, div, point, px, size,
|
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<Pixels> {
|
|
||||||
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.
|
/// Get the window resize edge.
|
||||||
fn resize_edge(
|
fn resize_edge(
|
||||||
pos: Point<Pixels>,
|
pos: Point<Pixels>,
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use state::{CoopAuthUrlHandler, NostrRegistry, USER_KEYRING};
|
|||||||
use theme::ActiveTheme;
|
use theme::ActiveTheme;
|
||||||
use ui::button::{Button, ButtonVariants};
|
use ui::button::{Button, ButtonVariants};
|
||||||
use ui::input::{Input, InputEvent, InputState};
|
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) {
|
pub fn open(window: &mut Window, cx: &mut App) {
|
||||||
let import = cx.new(|cx| ImportIdentity::new(window, cx));
|
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)
|
.show_close(false)
|
||||||
.overlay_closable(false)
|
.overlay_closable(false)
|
||||||
.keyboard(false)
|
.keyboard(false)
|
||||||
.title("Get Started")
|
.title("Continue with your Nostr Identity")
|
||||||
.child(import.clone())
|
.child(import.clone())
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -29,20 +29,13 @@ pub fn open(window: &mut Window, cx: &mut App) {
|
|||||||
pub struct ImportIdentity {
|
pub struct ImportIdentity {
|
||||||
/// Secret key input
|
/// Secret key input
|
||||||
key_input: Entity<InputState>,
|
key_input: Entity<InputState>,
|
||||||
|
|
||||||
/// Password input (if required)
|
/// Password input (if required)
|
||||||
pass_input: Entity<InputState>,
|
pass_input: Entity<InputState>,
|
||||||
|
|
||||||
/// Error message
|
/// Error message
|
||||||
error: Entity<Option<SharedString>>,
|
error: Entity<Option<SharedString>>,
|
||||||
|
|
||||||
/// Whether the user is currently loading
|
/// Whether the user is currently loading
|
||||||
loading: bool,
|
loading: bool,
|
||||||
|
|
||||||
/// Async tasks
|
|
||||||
tasks: Vec<Task<Result<(), Error>>>,
|
tasks: Vec<Task<Result<(), Error>>>,
|
||||||
|
|
||||||
/// Input subscription
|
|
||||||
_subscription: Option<Subscription>,
|
_subscription: Option<Subscription>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,7 +223,7 @@ impl Render for ImportIdentity {
|
|||||||
|
|
||||||
v_flex()
|
v_flex()
|
||||||
.size_full()
|
.size_full()
|
||||||
.gap_4()
|
.gap_2()
|
||||||
.text_sm()
|
.text_sm()
|
||||||
.child(
|
.child(
|
||||||
v_flex()
|
v_flex()
|
||||||
@@ -279,12 +272,11 @@ impl Render for ImportIdentity {
|
|||||||
this.login(window, cx);
|
this.login(window, cx);
|
||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
.child(divider(cx))
|
|
||||||
.when(!is_wasm, |this| {
|
.when(!is_wasm, |this| {
|
||||||
this.child(
|
this.child(
|
||||||
Button::new("proxy")
|
Button::new("proxy")
|
||||||
.label("Connect via Web Extension (Experimental)")
|
.label("Connect via Web Extension (Experimental)")
|
||||||
.ghost_alt()
|
.ghost()
|
||||||
.loading(self.loading)
|
.loading(self.loading)
|
||||||
.disabled(self.loading)
|
.disabled(self.loading)
|
||||||
.on_click(cx.listener(move |this, _ev, _window, cx| {
|
.on_click(cx.listener(move |this, _ev, _window, cx| {
|
||||||
|
|||||||
@@ -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 nip29::{GroupReference, GroupsRegistry};
|
||||||
use ui::WindowExtension;
|
use ui::button::{Button, ButtonVariants};
|
||||||
use ui::input::{Input, InputState};
|
use ui::input::{Input, InputState};
|
||||||
use ui::notification::Notification;
|
use ui::notification::Notification;
|
||||||
|
use ui::{IconName, StyledExt, WindowExtension, h_flex, v_flex};
|
||||||
|
|
||||||
|
type SubmitFn = Rc<dyn Fn(&ClickEvent, &mut Window, &mut App) -> bool>;
|
||||||
|
|
||||||
pub fn open(window: &mut Window, cx: &mut App) {
|
pub fn open(window: &mut Window, cx: &mut App) {
|
||||||
let input = cx.new(|cx| InputState::new(window, cx).placeholder("naddr1…"));
|
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();
|
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.))
|
this.width(px(380.))
|
||||||
.confirm()
|
|
||||||
.title("Join Group")
|
.title("Join Group")
|
||||||
.child(Input::new(&input).cleanable(true).flex_1().text_sm())
|
.on_ok({
|
||||||
.on_ok(move |_event, window, cx| {
|
let join = join.clone();
|
||||||
let groups = GroupsRegistry::global(cx);
|
move |event, window, cx| join(event, window, 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
|
|
||||||
})
|
})
|
||||||
|
.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,11 +16,10 @@ use state::NostrRegistry;
|
|||||||
use theme::ActiveTheme;
|
use theme::ActiveTheme;
|
||||||
use ui::avatar::Avatar;
|
use ui::avatar::Avatar;
|
||||||
use ui::button::{Button, ButtonVariants};
|
use ui::button::{Button, ButtonVariants};
|
||||||
use ui::dialog::DialogButtonProps;
|
|
||||||
use ui::dock::{DockArea, DockPlacement, PanelHandle};
|
use ui::dock::{DockArea, DockPlacement, PanelHandle};
|
||||||
use ui::input::{Input, InputEvent, InputState};
|
use ui::input::{Input, InputEvent, InputState};
|
||||||
use ui::nav::Nav;
|
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.
|
/// Maximum number of members a chat can have.
|
||||||
const MAX_MEMBERS: usize = 3;
|
const MAX_MEMBERS: usize = 3;
|
||||||
@@ -30,21 +29,7 @@ pub fn open(dock: WeakEntity<DockArea>, window: &mut Window, cx: &mut App) {
|
|||||||
|
|
||||||
window.open_dialog(cx, {
|
window.open_dialog(cx, {
|
||||||
let view = view.clone();
|
let view = view.clone();
|
||||||
move |this, _window, _cx| {
|
move |this, _window, _cx| this.width(px(420.)).title("New chat").child(view.clone())
|
||||||
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())
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -452,5 +437,28 @@ impl Render for NewChat {
|
|||||||
)
|
)
|
||||||
.h_80(),
|
.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);
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 nip29::{GroupId, GroupKey, GroupMetadata, GroupsRegistry};
|
||||||
use nostr_sdk::prelude::*;
|
use nostr_sdk::prelude::*;
|
||||||
use theme::ActiveTheme;
|
use theme::ActiveTheme;
|
||||||
|
use ui::button::{Button, ButtonVariants};
|
||||||
use ui::input::{Input, InputState};
|
use ui::input::{Input, InputState};
|
||||||
use ui::notification::Notification;
|
use ui::notification::Notification;
|
||||||
use ui::{WindowExtension, v_flex};
|
use ui::{IconName, StyledExt, WindowExtension, h_flex, v_flex};
|
||||||
|
|
||||||
|
type SubmitFn = Rc<dyn Fn(&ClickEvent, &mut Window, &mut App) -> bool>;
|
||||||
|
|
||||||
pub fn open(window: &mut Window, cx: &mut App) {
|
pub fn open(window: &mut Window, cx: &mut App) {
|
||||||
let name = cx.new(|cx| InputState::new(window, cx).placeholder("Group name"));
|
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 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"));
|
let relay = cx.new(|cx| InputState::new(window, cx).placeholder("wss://relay.example.com"));
|
||||||
|
|
||||||
window.open_dialog(cx, move |this, _window, cx| {
|
let submit: SubmitFn = {
|
||||||
let name = name.clone();
|
let (name, id, relay) = (name.clone(), id.clone(), relay.clone());
|
||||||
let id = id.clone();
|
Rc::new(move |_, window, cx| create(&name, &id, &relay, window, cx))
|
||||||
let relay = relay.clone();
|
};
|
||||||
|
|
||||||
|
window.open_dialog(cx, move |this, _window, cx| {
|
||||||
this.width(px(380.))
|
this.width(px(380.))
|
||||||
.confirm()
|
|
||||||
.title("New group")
|
.title("New group")
|
||||||
|
.on_ok({
|
||||||
|
let submit = submit.clone();
|
||||||
|
move |event, window, cx| submit(event, window, cx)
|
||||||
|
})
|
||||||
.child(
|
.child(
|
||||||
v_flex()
|
v_flex()
|
||||||
.gap_3()
|
.gap_3()
|
||||||
@@ -26,7 +35,32 @@ pub fn open(window: &mut Window, cx: &mut App) {
|
|||||||
.child(field("Group id", &id, cx))
|
.child(field("Group id", &id, cx))
|
||||||
.child(field("Relay", &relay, 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use nostr_sdk::prelude::*;
|
|||||||
use theme::ActiveTheme;
|
use theme::ActiveTheme;
|
||||||
use ui::button::{Button, ButtonVariants};
|
use ui::button::{Button, ButtonVariants};
|
||||||
use ui::input::{Input, InputEvent, InputState};
|
use ui::input::{Input, InputEvent, InputState};
|
||||||
use ui::{WindowExtension, v_flex};
|
use ui::{IconName, StyledExt, WindowExtension, h_flex, v_flex};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct RestoreEncryption {
|
pub struct RestoreEncryption {
|
||||||
@@ -99,7 +99,7 @@ impl Render for RestoreEncryption {
|
|||||||
fn render(&mut self, _window: &mut gpui::Window, cx: &mut Context<Self>) -> impl IntoElement {
|
fn render(&mut self, _window: &mut gpui::Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
v_flex()
|
v_flex()
|
||||||
.size_full()
|
.size_full()
|
||||||
.gap_2()
|
.gap_3()
|
||||||
.text_sm()
|
.text_sm()
|
||||||
.child(
|
.child(
|
||||||
v_flex()
|
v_flex()
|
||||||
@@ -110,12 +110,16 @@ impl Render for RestoreEncryption {
|
|||||||
.child(Input::new(&self.key_input)),
|
.child(Input::new(&self.key_input)),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
Button::new("restore")
|
h_flex().gap_1().justify_end().child(
|
||||||
.label("Restore")
|
Button::new("restore")
|
||||||
.primary()
|
.icon(IconName::CheckCircle)
|
||||||
.on_click(cx.listener(move |this, _, window, cx| {
|
.label("Restore")
|
||||||
this.restore(window, cx);
|
.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| {
|
.when_some(self.error.read(cx).as_ref(), |this, error| {
|
||||||
this.child(
|
this.child(
|
||||||
|
|||||||
@@ -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))
|
cx.new(|cx| Screening::new(public_key, window, cx))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Screening
|
|
||||||
pub struct Screening {
|
pub struct Screening {
|
||||||
/// Public Key of the person being screened.
|
/// Public Key of the person being screened.
|
||||||
public_key: PublicKey,
|
public_key: PublicKey,
|
||||||
|
|
||||||
/// Whether the person's address is verified.
|
/// Whether the person's address is verified.
|
||||||
verified: bool,
|
verified: bool,
|
||||||
|
|
||||||
/// Whether the person is followed by current user.
|
/// Whether the person is followed by current user.
|
||||||
followed: bool,
|
followed: bool,
|
||||||
|
|
||||||
/// Last time the person was active.
|
/// Last time the person was active.
|
||||||
last_active: Option<Timestamp>,
|
last_active: Option<Timestamp>,
|
||||||
|
|
||||||
/// All mutual contacts of the person being screened.
|
/// All mutual contacts of the person being screened.
|
||||||
mutual_contacts: Vec<PublicKey>,
|
mutual_contacts: Vec<PublicKey>,
|
||||||
|
|
||||||
/// Async tasks
|
|
||||||
tasks: SmallVec<[Task<()>; 3]>,
|
tasks: SmallVec<[Task<()>; 3]>,
|
||||||
|
|
||||||
/// Subscriptions
|
|
||||||
_subscriptions: SmallVec<[Subscription; 1]>,
|
_subscriptions: SmallVec<[Subscription; 1]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user