migrate ui components to gpui-kit

This commit is contained in:
2026-10-21 13:40:29 +07:00
parent 0d4acf5b65
commit d0ad1a6f57
173 changed files with 2775 additions and 10885 deletions
+81 -72
View File
@@ -6,19 +6,21 @@ use gpui::{
Styled, Subscription, Task, WeakEntity, Window, div, px, rems,
};
use gpui_base::{Popup, Select};
use gpui_kit::assets::IconName;
use gpui_kit::component::ActiveTheme;
use gpui_kit::component::avatar::Avatar;
use gpui_kit::component::button::{Button, ButtonVariants};
use gpui_kit::component::dock::{DockArea, DockPlacement, PanelHandle};
use gpui_kit::component::form::field;
use gpui_kit::component::input::{Input, InputEvent, InputState, Textarea, TextareaState};
use gpui_kit::component::switch::Switch;
use gpui_kit::component::{Disableable, Icon, Sizable, WindowExt, h_flex, v_flex};
use nip29::{GroupCreateOptions, GroupKey, GroupsEvent, GroupsRegistry};
use nostr_sdk::prelude::*;
use settings::AppSettings;
use state::upload;
use theme::{ActiveTheme, TextStyle, Typeset as _, button_radius, control_radius};
use ui::avatar::Avatar;
use ui::button::{Button, ButtonVariants};
use ui::dock::{DockArea, DockPlacement, PanelHandle};
use ui::input::{Input, InputEvent, InputState, Textarea, TextareaState};
use ui::switch::Switch;
use ui::{
Disableable, Icon, IconName, Sizable, StyledExt, WindowExtension, divider, h_flex, v_flex,
};
use ui::divider;
use ui::{TextStyle, Typeset as _, button_radius, control_radius};
const DEFAULT_RELAYS: [&str; 2] = ["wss://relay.ditto.net", "wss://chat.wisp.talk"];
const PRIVATE_DESC: &str = "Only members can read group messages";
@@ -257,7 +259,7 @@ impl NewGroup {
let registry = GroupsRegistry::global(cx);
let group = registry.update(cx, |registry, cx| registry.open(key, cx));
ui::dock::add_panel_to(
crate::dock::add_panel_to(
&self.dock,
PanelHandle::new(nip29_ui::init(group, window, cx)),
DockPlacement::Center,
@@ -348,7 +350,7 @@ impl NewGroup {
return v_flex().gap_1p5().child(field_label("Relay", cx)).child(
div()
.text_style(TextStyle::Caption)
.text_color(cx.theme().text_muted)
.text_color(cx.theme().muted_foreground)
.child(format!(
"Created on {} (same relay as the parent group).",
relay_label(parent.relay())
@@ -369,13 +371,13 @@ impl NewGroup {
.text_style(TextStyle::Callout)
.cursor_default()
.bg(if open {
cx.theme().element_active
cx.theme().list_active
} else {
cx.theme().surface_raised
cx.theme().secondary
})
.when(!open, |this| {
this.hover(|this| this.bg(cx.theme().element_hover))
.active(|this| this.bg(cx.theme().element_active))
this.hover(|this| this.bg(cx.theme().primary))
.active(|this| this.bg(cx.theme().list_active))
})
.on_click(cx.listener(move |this, _, _window, cx| {
this.relay_select_open = !open;
@@ -385,7 +387,7 @@ impl NewGroup {
.child(
Icon::new(IconName::ChevronDown)
.small()
.text_color(cx.theme().text_muted),
.text_color(cx.theme().muted_foreground),
);
let select = Select::new("relay-select")
@@ -412,11 +414,15 @@ impl NewGroup {
.on_mouse_down_out(cx.listener(|this, _, _, cx| {
this.set_relay_select_open(false, cx);
}))
.popover_style(cx)
.bg(cx.theme().popover)
.border_1()
.border_color(cx.theme().border)
.shadow_md()
.rounded(cx.theme().radius_lg)
.p_1()
.gap_y_1()
.min_w(rems(8.))
.text_color(cx.theme().text)
.text_color(cx.theme().foreground)
.children(self.relays.iter().enumerate().map(|(index, relay)| {
h_flex()
.id(SharedString::from(format!("option-{relay}")))
@@ -425,7 +431,7 @@ impl NewGroup {
.rounded(px(button_radius()))
.text_style(TextStyle::Callout)
.cursor_default()
.hover(|this| this.bg(cx.theme().element_hover))
.hover(|this| this.bg(cx.theme().primary))
.child(relay_label(relay))
.on_click(cx.listener(move |this, _, _, cx| {
this.activate_relay_option(index, cx);
@@ -441,7 +447,7 @@ impl NewGroup {
.text_style(TextStyle::Callout)
.cursor_default()
.child("Custom relay")
.hover(|this| this.bg(cx.theme().element_hover))
.hover(|this| this.bg(cx.theme().primary))
.on_click(cx.listener(|this, _, _, cx| {
this.activate_relay_option(this.relays.len(), cx);
})),
@@ -457,41 +463,44 @@ impl NewGroup {
}
fn render_access(&self, cx: &mut Context<Self>) -> impl IntoElement {
let private = Switch::new("private")
.label("Private")
.description(PRIVATE_DESC)
.checked(self.private)
.on_click(cx.listener(|this, checked: &bool, _window, cx| {
this.private = *checked;
cx.notify();
}));
let private = field().label("Private").description(PRIVATE_DESC).child(
Switch::new("private")
.checked(self.private)
.on_click(cx.listener(|this, checked: &bool, _window, cx| {
this.private = *checked;
cx.notify();
})),
);
let closed = Switch::new("closed")
.label("Closed")
.description(CLOSED_DESC)
.checked(self.closed)
.on_click(cx.listener(|this, checked: &bool, _window, cx| {
this.closed = *checked;
cx.notify();
}));
let closed = field().label("Closed").description(CLOSED_DESC).child(
Switch::new("closed")
.checked(self.closed)
.on_click(cx.listener(|this, checked: &bool, _window, cx| {
this.closed = *checked;
cx.notify();
})),
);
let restricted = Switch::new("restricted")
let restricted = field()
.label("Restricted")
.description(RESTRICTED_DESC)
.checked(self.restricted)
.on_click(cx.listener(|this, checked: &bool, _window, cx| {
this.restricted = *checked;
cx.notify();
}));
.child(
Switch::new("restricted")
.checked(self.restricted)
.on_click(cx.listener(|this, checked: &bool, _window, cx| {
this.restricted = *checked;
cx.notify();
})),
);
let hidden = Switch::new("hidden")
.label("Hidden")
.description(HIDDEN_DESC)
.checked(self.hidden)
.on_click(cx.listener(|this, checked: &bool, _window, cx| {
this.hidden = *checked;
cx.notify();
}));
let hidden = field().label("Hidden").description(HIDDEN_DESC).child(
Switch::new("hidden")
.checked(self.hidden)
.on_click(cx.listener(|this, checked: &bool, _window, cx| {
this.hidden = *checked;
cx.notify();
})),
);
v_flex()
.gap_2()
@@ -503,14 +512,17 @@ impl NewGroup {
}
fn render_list_setting(&self, cx: &mut Context<Self>) -> impl IntoElement {
let list_private = Switch::new("list-private")
let list_private = field()
.label("Private on my list")
.description(LIST_PRIVATE_DESC)
.checked(self.list_private)
.on_click(cx.listener(|this, checked: &bool, _window, cx| {
this.list_private = *checked;
cx.notify();
}));
.child(
Switch::new("list-private")
.checked(self.list_private)
.on_click(cx.listener(|this, checked: &bool, _window, cx| {
this.list_private = *checked;
cx.notify();
})),
);
v_flex()
.gap_2()
@@ -560,14 +572,6 @@ impl NewGroup {
impl Render for NewGroup {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let name = self.name_input.read(cx).value().trim().to_owned();
let seed: String = if name.is_empty() {
"group".to_owned()
} else {
name
};
let selected = self
.selected_relay
.as_ref()
@@ -593,7 +597,12 @@ impl Render for NewGroup {
h_flex()
.items_center()
.gap_3()
.child(Avatar::new(self.picture.clone()).seed(seed).large())
.child(match self.picture.clone() {
Some(picture) => {
Avatar::new().src(picture).large().into_any_element()
}
None => Avatar::new().large().into_any_element(),
})
.child(
Button::new("upload-picture")
.label(if self.picture.is_some() {
@@ -610,9 +619,9 @@ impl Render for NewGroup {
})),
),
)
.child(field("Name", Input::new(&self.name_input), cx))
.child(input_field("Name", Input::new(&self.name_input), cx))
.child(self.render_relay(&selected, cx))
.child(field("About", Textarea::new(&self.about_input), cx))
.child(input_field("About", Textarea::new(&self.about_input), cx))
.child(self.render_access(cx))
.child(self.render_list_setting(cx))
.overflow_y_scroll(),
@@ -635,7 +644,7 @@ impl Render for NewGroup {
"Create Group"
})
.primary()
.font_semibold()
.font_weight(gpui::FontWeight::SEMIBOLD)
.loading(self.creating)
.disabled(self.creating || self.uploading)
.on_click(cx.listener(|this, _event, _window, cx| this.create(cx))),
@@ -644,15 +653,15 @@ impl Render for NewGroup {
}
}
fn field(text: &'static str, input: impl IntoElement, cx: &App) -> Div {
fn input_field(text: &'static str, input: impl IntoElement, cx: &App) -> Div {
v_flex().gap_1p5().child(field_label(text, cx)).child(input)
}
fn field_label(text: &'static str, cx: &App) -> Div {
div()
.text_style(TextStyle::Caption)
.text_color(cx.theme().text_muted)
.font_semibold()
.text_color(cx.theme().muted_foreground)
.font_weight(gpui::FontWeight::SEMIBOLD)
.child(text)
}
@@ -661,7 +670,7 @@ fn switch_row(switch: impl IntoElement, cx: &App) -> Div {
.w_full()
.p_2()
.rounded(px(control_radius()))
.bg(cx.theme().surface_raised)
.bg(cx.theme().secondary)
.child(switch)
}