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
+21 -19
View File
@@ -2,15 +2,15 @@ use gpui::{
AnyElement, App, IntoElement, ParentElement, SharedString, Styled, WeakEntity, Window, div, px,
relative,
};
use gpui_kit::component::avatar::Avatar;
use gpui_kit::component::{ActiveTheme, Sizable, WindowExt, h_flex, v_flex};
use nip29::{Group, GroupMetadata};
use theme::{ActiveTheme, TextStyle, Typeset as _, control_radius};
use ui::avatar::Avatar;
use ui::{Sizable, StyledExt, WindowExtension, h_flex, v_flex};
use ui::{TextStyle, Typeset as _, control_radius};
pub(crate) fn open(group: WeakEntity<Group>, window: &mut Window, cx: &mut App) {
window.open_dialog(cx, move |this, _window, cx| {
this.max_h(px(600.))
.show_close(true)
.close_button(true)
.title("About Group")
.child(body(&group, cx))
});
@@ -22,13 +22,16 @@ fn body(group: &WeakEntity<Group>, cx: &App) -> AnyElement {
};
let group = entity.read(cx);
let mut body = v_flex().gap_3().text_style(TextStyle::Callout).child(header(group, cx));
let mut body = v_flex()
.gap_3()
.text_style(TextStyle::Callout)
.child(header(group, cx));
if let Some(metadata) = group.metadata() {
if let Some(about) = metadata.about.as_ref() {
body = body.child(
div()
.text_color(cx.theme().text_muted)
.text_color(cx.theme().muted_foreground)
.child(SharedString::from(about.clone())),
);
}
@@ -76,7 +79,7 @@ fn body(group: &WeakEntity<Group>, cx: &App) -> AnyElement {
"Supported kinds",
if items.is_empty() {
div()
.text_color(cx.theme().text_faint)
.text_color(cx.theme().muted_foreground)
.child("None")
.into_any_element()
} else {
@@ -107,7 +110,7 @@ fn body(group: &WeakEntity<Group>, cx: &App) -> AnyElement {
.map(|pin| {
div()
.truncate()
.text_color(cx.theme().text_muted)
.text_color(cx.theme().muted_foreground)
.child(crate::util::pin_label(group, pin))
.into_any_element()
})
@@ -127,25 +130,24 @@ fn header(group: &Group, cx: &App) -> AnyElement {
h_flex()
.gap_3()
.items_center()
.child(
Avatar::from_source(group.display_image())
.seed(key.cache_tag())
.large(),
)
.child(match group.display_image() {
Some(picture) => Avatar::new().src(picture).large().into_any_element(),
None => Avatar::new().large().into_any_element(),
})
.child(
v_flex()
.min_w_0()
.child(
div()
.text_style(TextStyle::Body)
.font_semibold()
.font_weight(gpui::FontWeight::SEMIBOLD)
.truncate()
.child(group.display_name()),
)
.child(
div()
.text_style(TextStyle::Caption)
.text_color(cx.theme().text_faint)
.text_color(cx.theme().muted_foreground)
.truncate()
.child(SharedString::from(format!(
"{} · {}",
@@ -163,8 +165,8 @@ fn section(label: &'static str, content: AnyElement, cx: &App) -> AnyElement {
.child(
div()
.text_style(TextStyle::Caption)
.font_medium()
.text_color(cx.theme().text_muted)
.font_weight(gpui::FontWeight::MEDIUM)
.text_color(cx.theme().muted_foreground)
.child(label),
)
.child(content)
@@ -184,8 +186,8 @@ fn chip(label: impl Into<SharedString>, cx: &App) -> AnyElement {
.px_2()
.py_1()
.rounded(px(control_radius()))
.bg(cx.theme().surface_raised)
.text_color(cx.theme().text)
.bg(cx.theme().secondary)
.text_color(cx.theme().foreground)
.text_style(TextStyle::Caption)
.line_height(relative(1.))
.child(label.into())
+9 -9
View File
@@ -5,13 +5,13 @@ use gpui::{
App, AppContext, ClipboardItem, Entity, IntoElement, ParentElement, SharedString, Styled,
WeakEntity, Window, div, px,
};
use gpui_kit::component::button::{Button, ButtonVariants};
use gpui_kit::component::input::InputState;
use gpui_kit::component::notification::Notification;
use gpui_kit::component::{ActiveTheme, Sizable, WindowExt, h_flex, v_flex};
use nip29::{Group, Invite};
use nostr_sdk::prelude::SecretKey;
use theme::{ActiveTheme, TextStyle, Typeset as _};
use ui::button::{Button, ButtonVariants};
use ui::input::InputState;
use ui::notification::Notification;
use ui::{Sizable, WindowExtension, h_flex, v_flex};
use ui::{TextStyle, Typeset as _};
use crate::util::{field, report};
@@ -26,7 +26,7 @@ pub(crate) fn open(group: WeakEntity<Group>, window: &mut Window, cx: &mut App)
window.open_dialog(cx, move |this, _window, cx| {
this.max_h(px(600.))
.show_close(true)
.close_button(true)
.title("Invites")
.child(body(group.clone(), input.clone(), cx))
});
@@ -51,13 +51,13 @@ fn body(group: WeakEntity<Group>, input: Entity<InputState>, cx: &App) -> impl I
.text_style(TextStyle::Callout)
.child(
div()
.text_color(cx.theme().text_muted)
.text_color(cx.theme().muted_foreground)
.child("Anyone with a code can ask to join. Revoking a code stops new joins, it doesn't remove anyone."),
)
.when(invites.is_empty(), |this| {
this.child(
div()
.text_color(cx.theme().text_faint)
.text_color(cx.theme().muted_foreground)
.child("No active invites"),
)
})
@@ -69,7 +69,7 @@ fn invite_row(group: WeakEntity<Group>, invite: &Invite, cx: &App) -> impl IntoE
let created = div()
.flex_shrink_0()
.text_style(TextStyle::Caption)
.text_color(cx.theme().text_faint)
.text_color(cx.theme().muted_foreground)
.child(invite.created_at.to_ago());
h_flex()
+91 -89
View File
@@ -1,14 +1,24 @@
use std::collections::BTreeMap;
use anyhow::Result;
use chat_ui::text::rendered_text;
use chat_ui::message::WelcomeMessage;
use chat_ui::text::{RenderedText, rendered_text};
use gpui::prelude::FluentBuilder;
use gpui::{
Action, AnyElement, App, AppContext, Context, Entity, EventEmitter, FocusHandle, Focusable,
FollowMode, ImageSource, IntoElement, ListAlignment, ListScrollEvent, ListState, ParentElement,
FollowMode, IntoElement, ListAlignment, ListScrollEvent, ListState, ParentElement,
PathPromptOptions, Render, SharedString, Styled, Subscription, Task, WeakEntity, Window, div,
list, px,
};
use gpui_kit::assets::IconName;
use gpui_kit::component::avatar::Avatar;
use gpui_kit::component::button::{Button, ButtonVariant, ButtonVariants};
use gpui_kit::component::dialog::DialogButtonProps;
use gpui_kit::component::dock::{Panel, PanelEvent};
use gpui_kit::component::input::{InputEvent, Textarea, TextareaState};
use gpui_kit::component::notification::Notification;
use gpui_kit::component::scroll::Scrollbar;
use gpui_kit::component::{ActiveTheme, Disableable, Icon, Sizable, WindowExt, h_flex, v_flex};
use nip29::{Group, GroupEvent, GroupKey, GroupsRegistry, Membership, Pin, Row};
use nostr_sdk::prelude::*;
use person::Directory;
@@ -16,17 +26,7 @@ use serde::Deserialize;
use settings::AppSettings;
use smallvec::{SmallVec, smallvec};
use state::{NostrRegistry, upload};
use theme::{ActiveTheme, TextStyle, Typeset as _, control_radius};
use ui::avatar::Avatar;
use ui::button::{Button, ButtonVariant, ButtonVariants};
use ui::dialog::DialogButtonProps;
use ui::dock::{Panel, PanelEvent};
use ui::input::{InputEvent, Textarea, TextareaState};
use ui::markdown::RenderedText;
use ui::message::WelcomeMessage;
use ui::notification::Notification;
use ui::scroll::Scrollbar;
use ui::{Disableable, Icon, IconName, Sizable, StyledExt, WindowExtension, h_flex, v_flex};
use ui::{TextStyle, Typeset as _, control_radius};
use util::{attachment_name, display_name, opens_run, pin_label, report};
mod details;
@@ -123,7 +123,7 @@ pub fn actions(group: WeakEntity<Group>, cx: &App) -> AnyElement {
.when(admin, |this| {
this.child(
Button::new("edit")
.icon(IconName::Edit)
.icon(IconName::SquarePen)
.tooltip("Edit group")
.ghost()
.small()
@@ -133,7 +133,7 @@ pub fn actions(group: WeakEntity<Group>, cx: &App) -> AnyElement {
)
.child(
Button::new("group-invite")
.icon(IconName::Invite)
.icon(IconName::UserRoundPlus)
.tooltip("Invite people")
.ghost()
.small()
@@ -164,7 +164,7 @@ pub fn actions(group: WeakEntity<Group>, cx: &App) -> AnyElement {
)
.child(
Button::new("group-delete")
.icon(IconName::CloseCircle)
.icon(IconName::CircleX)
.tooltip("Delete group")
.ghost()
.small()
@@ -175,7 +175,7 @@ pub fn actions(group: WeakEntity<Group>, cx: &App) -> AnyElement {
})
.child(
Button::new("leave")
.icon(IconName::Logout)
.icon(IconName::LogOut)
.tooltip("Leave group")
.ghost()
.small()
@@ -195,7 +195,7 @@ pub fn confirm_leave(group: WeakEntity<Group>, window: &mut Window, cx: &mut App
return;
};
window.open_dialog(cx, move |this, _window, _cx| {
window.open_alert_dialog(cx, move |this, _window, _cx| {
let key = key.clone();
this.confirm()
@@ -231,7 +231,7 @@ pub fn confirm_delete(group: WeakEntity<Group>, window: &mut Window, cx: &mut Ap
return;
};
window.open_dialog(cx, move |this, _window, _cx| {
window.open_alert_dialog(cx, move |this, _window, _cx| {
let key = key.clone();
this.confirm()
@@ -305,7 +305,6 @@ fn put_user(
}
pub struct GroupPanel {
id: SharedString,
focus_handle: FocusHandle,
group: WeakEntity<Group>,
/// Person directory for members, message authors, and activity subjects
@@ -328,16 +327,11 @@ pub struct GroupPanel {
impl GroupPanel {
pub(crate) fn new(group: Entity<Group>, window: &mut Window, cx: &mut Context<Self>) -> Self {
let (id, name, count, head) = {
let (name, count, head) = {
let group = group.read(cx);
let rows = group.rows();
(
SharedString::from(format!("group-{}", group.key().cache_tag())),
display_name(group),
rows.len(),
rows.first().map(Row::id),
)
(display_name(group), rows.len(), rows.first().map(Row::id))
};
let input = cx.new(|cx| {
@@ -383,7 +377,6 @@ impl GroupPanel {
});
let mut this = Self {
id,
focus_handle: cx.focus_handle(),
group: group.downgrade(),
directory: Directory::new(cx),
@@ -721,7 +714,7 @@ impl GroupPanel {
h_flex()
.flex_shrink_0()
.w_full()
.gap(px(theme::SPACE))
.gap(px(ui::SPACE))
.px_3()
.py_2()
.text_style(TextStyle::Callout)
@@ -730,7 +723,7 @@ impl GroupPanel {
.child(
Icon::new(IconName::Info)
.small()
.text_color(cx.theme().text_faint),
.text_color(cx.theme().muted_foreground),
)
.child(div().flex_1().min_w_0().truncate().child(message))
.child(
@@ -759,11 +752,11 @@ impl GroupPanel {
h_flex()
.flex_shrink_0()
.w_full()
.gap(px(theme::SPACE))
.gap(px(ui::SPACE))
.px_3()
.py_2()
.text_style(TextStyle::Callout)
.bg(cx.theme().surface_raised)
.bg(cx.theme().secondary)
.child(
Icon::new(IconName::Info)
.small()
@@ -788,7 +781,7 @@ impl GroupPanel {
)
.child(
Button::new("dismiss")
.icon(IconName::Close)
.icon(IconName::X)
.small()
.ghost()
.tooltip("Close")
@@ -827,9 +820,9 @@ impl GroupPanel {
.px_1p5()
.py_0p5()
.rounded(px(control_radius()))
.bg(cx.theme().solid)
.bg(cx.theme().primary)
.text_style(TextStyle::Caption)
.text_color(cx.theme().text_muted)
.text_color(cx.theme().muted_foreground)
.child(pin_label(group, pin))
}))
.into_any_element()
@@ -839,17 +832,22 @@ impl GroupPanel {
let (name, avatar) = match self.group.upgrade() {
Some(group) => {
let group = group.read(cx);
let seed = group.key().cache_tag();
let picture = group
.metadata()
.and_then(|metadata| metadata.picture.clone());
(
display_name(group),
Avatar::from_source(picture).seed(seed).large(),
match picture {
Some(picture) => Avatar::new().src(picture).large().into_any_element(),
None => Avatar::new().large().into_any_element(),
},
)
}
None => (SharedString::from("this group"), Avatar::new(None)),
None => (
SharedString::from("this group"),
Avatar::new().large().into_any_element(),
),
};
WelcomeMessage::new("welcome")
@@ -906,11 +904,11 @@ impl GroupPanel {
.w_full()
.justify_center()
.items_center()
.gap(px(theme::SPACE))
.gap(px(ui::SPACE))
.px_3()
.py_2()
.text_style(TextStyle::Callout)
.text_color(cx.theme().text_muted);
.text_color(cx.theme().muted_foreground);
match notice {
Notice::Relay(reason) => row
@@ -941,7 +939,7 @@ impl GroupPanel {
Button::new("join")
.label("Request to Join")
.small()
.ghost_alt()
.ghost()
.on_click(cx.listener(move |this, _event, _window, cx| {
this.request_join(key.clone(), cx);
})),
@@ -1079,7 +1077,7 @@ impl GroupPanel {
.child(Textarea::new(&self.input).appearance(false).flex_1())
.child(
Button::new("send")
.icon(IconName::PaperPlaneFill)
.icon(IconName::SendHorizontal)
.tooltip("Send")
.ghost()
.large()
@@ -1120,12 +1118,12 @@ impl GroupPanel {
.px_2()
.py_1()
.rounded(px(control_radius()))
.bg(cx.theme().surface_raised)
.bg(cx.theme().secondary)
.text_style(TextStyle::Caption)
.child(
div()
.flex_shrink_0()
.text_color(cx.theme().text_muted)
.text_color(cx.theme().muted_foreground)
.child("Replying to"),
)
.child(
@@ -1133,8 +1131,8 @@ impl GroupPanel {
.flex_shrink_0()
.max_w(px(140.))
.truncate()
.font_semibold()
.text_color(cx.theme().text)
.font_weight(gpui::FontWeight::SEMIBOLD)
.text_color(cx.theme().foreground)
.child(author),
)
.child(
@@ -1142,12 +1140,12 @@ impl GroupPanel {
.min_w_0()
.flex_1()
.truncate()
.text_color(cx.theme().text_faint)
.text_color(cx.theme().muted_foreground)
.child(snippet),
)
.child(
Button::new("cancel-reply")
.icon(IconName::Close)
.icon(IconName::X)
.tooltip("Cancel reply")
.ghost()
.small()
@@ -1171,10 +1169,10 @@ impl GroupPanel {
.h_8()
.px_2()
.border_1()
.border_color(cx.theme().border_faint)
.border_color(cx.theme().border)
.rounded(px(control_radius()))
.text_style(TextStyle::Caption)
.text_color(cx.theme().text_muted)
.text_color(cx.theme().muted_foreground)
.child(Icon::new(IconName::Link).xsmall())
.child(
div()
@@ -1184,7 +1182,7 @@ impl GroupPanel {
)
.child(
Button::new(SharedString::from(format!("remove-attachment-{ix}")))
.icon(IconName::Close)
.icon(IconName::X)
.tooltip("Remove")
.ghost()
.xsmall()
@@ -1198,45 +1196,12 @@ impl GroupPanel {
}
}
impl Panel for GroupPanel {
fn panel_id(&self) -> SharedString {
self.id.clone()
impl gpui_kit::component::dock::BasePanel for GroupPanel {
fn panel_name(&self) -> &'static str {
"GroupPanel"
}
fn title(&self, cx: &App) -> AnyElement {
self.group
.upgrade()
.map(|group| {
let group = group.read(cx);
let seed = group.key().cache_tag();
let picture = group
.metadata()
.and_then(|metadata| metadata.picture.clone());
h_flex()
.gap_1()
.text_style(TextStyle::Caption)
.child(Avatar::from_source(picture).seed(seed).xsmall())
.child(display_name(group))
.into_any_element()
})
.unwrap_or_else(|| {
div()
.text_style(TextStyle::Caption)
.child("Unknown")
.into_any_element()
})
}
fn toolbar_buttons(&self, _window: &Window, _cx: &App) -> Vec<Button> {
Vec::new()
}
fn background(&self, cx: &App) -> Option<ImageSource> {
AppSettings::panel_background_source(cx)
}
fn set_active(&self, active: bool, window: &mut Window, cx: &mut App) {
fn set_active(&mut self, active: bool, window: &mut Window, cx: &mut Context<Self>) {
let Some(group) = self.group.upgrade() else {
return;
};
@@ -1262,6 +1227,43 @@ impl Panel for GroupPanel {
}
}
impl Panel for GroupPanel {
fn title(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
self.group
.upgrade()
.map(|group| {
let group = group.read(cx);
let picture = group
.metadata()
.and_then(|metadata| metadata.picture.clone());
h_flex()
.gap_1()
.text_style(TextStyle::Caption)
.child(match picture {
Some(picture) => Avatar::new().src(picture).xsmall().into_any_element(),
None => Avatar::new().xsmall().into_any_element(),
})
.child(display_name(group))
.into_any_element()
})
.unwrap_or_else(|| {
div()
.text_style(TextStyle::Caption)
.child("Unknown")
.into_any_element()
})
}
fn toolbar_buttons(
&mut self,
_window: &mut Window,
_cx: &mut Context<Self>,
) -> Option<Vec<Button>> {
None
}
}
impl EventEmitter<PanelEvent> for GroupPanel {}
impl Focusable for GroupPanel {
@@ -1295,7 +1297,7 @@ impl Render for GroupPanel {
.size_full()
.justify_center()
.text_style(TextStyle::Callout)
.text_color(cx.theme().text_faint)
.text_color(cx.theme().muted_foreground)
.child("No messages yet")
.into_any_element(),
}),
+38 -33
View File
@@ -1,20 +1,21 @@
use chat::Message;
use chat_ui::message::{MessageMedia, MessageRow, ReactionBar, ReplyPreview, group_reactions};
use common::TimestampExt;
use gpui::prelude::FluentBuilder;
use gpui::{
AnyElement, App, Entity, InteractiveElement, IntoElement, ParentElement, SharedString, Styled,
WeakEntity, div, px,
};
use gpui_kit::assets::IconName;
use gpui_kit::component::avatar::Avatar;
use gpui_kit::component::menu::{ContextMenuExt as _, PopupMenu, PopupMenuItem};
use gpui_kit::component::{ActiveTheme, Icon, Sizable, h_flex};
use nip29::{Activity, Group, Reaction};
use nostr_sdk::prelude::{EventId, Kind};
use person::Directory;
use settings::AppSettings;
use state::NostrRegistry;
use theme::{ActiveTheme, TextStyle, Typeset as _};
use ui::avatar::Avatar;
use ui::menu::{ContextMenu, PopupMenu, PopupMenuItem};
use ui::message::{MessageMedia, MessageRow, ReactionBar, ReplyPreview, group_reactions};
use ui::{Icon, IconName, Sizable, StyledExt, h_flex};
use ui::{TextStyle, Typeset as _};
use crate::GroupPanel;
@@ -57,11 +58,13 @@ pub(crate) fn render(
let row = MessageRow::new(ix)
.show_author(show_author)
.hide_avatar(hide_avatar)
.avatar(
Avatar::new(author.avatar())
.seed(author.avatar_seed())
.flex_shrink_0(),
)
.avatar(match author.avatar() {
Some(picture) => Avatar::new()
.src(picture)
.flex_shrink_0()
.into_any_element(),
None => Avatar::new().flex_shrink_0().into_any_element(),
})
.author(author.name())
.timestamp(message.created_at.to_human_time())
.when(!message.replies_to.is_empty(), |this| {
@@ -79,8 +82,7 @@ pub(crate) fn render(
})
.when(!reaction_groups.is_empty(), |this| {
this.child(ReactionBar::new(reaction_groups))
})
.into_any_element();
});
let id = message.id;
let admin = actions.admin;
@@ -88,10 +90,11 @@ pub(crate) fn render(
let group = actions.group;
let panel = actions.panel;
ContextMenu::new(
format!("message-menu-{}", id.to_hex()),
row,
move |menu, window, cx| {
let row = div()
.id(SharedString::from(format!("message-menu-{}", id.to_hex())))
.w_full()
.child(row)
.context_menu(move |menu, window, cx| {
let menu = menu
.item({
let panel = panel.clone();
@@ -136,9 +139,9 @@ pub(crate) fn render(
});
})
})
},
)
.into_any_element()
});
row.into_any_element()
}
fn react_menu(
@@ -214,25 +217,27 @@ pub(crate) fn render_activity(
h_flex()
.id(ix)
.w_full()
.gap(px(theme::SPACE))
.gap(px(ui::SPACE))
.py_1()
.px_3()
.hover(|this| this.bg(cx.theme().element_hover))
.hover(|this| this.bg(cx.theme().primary))
.child(badge(activity, cx))
.when(!hide_avatar, |this| {
this.child(
Avatar::new(subject.avatar())
.seed(subject.avatar_seed())
this.child(match subject.avatar() {
Some(picture) => Avatar::new()
.src(picture)
.small()
.flex_shrink_0(),
)
.flex_shrink_0()
.into_any_element(),
None => Avatar::new().small().flex_shrink_0().into_any_element(),
})
})
.child(
div()
.min_w_0()
.truncate()
.text_style(TextStyle::Callout)
.font_semibold()
.font_weight(gpui::FontWeight::SEMIBOLD)
.child(subject.name()),
)
.child(
@@ -240,7 +245,7 @@ pub(crate) fn render_activity(
.min_w_0()
.truncate()
.text_style(TextStyle::Callout)
.text_color(cx.theme().text_faint)
.text_color(cx.theme().muted_foreground)
.child(SharedString::from(action(activity))),
)
.child(
@@ -248,7 +253,7 @@ pub(crate) fn render_activity(
.flex_shrink_0()
.ml_1()
.text_style(TextStyle::Caption)
.text_color(cx.theme().text_faint)
.text_color(cx.theme().muted_foreground)
.child(activity.created_at.to_ago()),
)
.into_any_element()
@@ -256,11 +261,11 @@ pub(crate) fn render_activity(
fn badge(activity: &Activity, cx: &App) -> AnyElement {
let (icon, color) = match activity.kind {
Kind::GroupJoinRequest => (IconName::Login, cx.theme().accent),
Kind::GroupPutUser => (IconName::Login, cx.theme().accent),
Kind::GroupLeaveRequest => (IconName::Logout, cx.theme().danger),
Kind::GroupJoinRequest => (IconName::LogIn, cx.theme().primary),
Kind::GroupPutUser => (IconName::LogIn, cx.theme().primary),
Kind::GroupLeaveRequest => (IconName::LogOut, cx.theme().danger),
Kind::GroupRemoveUser => (IconName::Ban, cx.theme().danger),
_ => (IconName::Info, cx.theme().text_faint),
_ => (IconName::Info, cx.theme().muted_foreground),
};
h_flex()
+63 -38
View File
@@ -6,15 +6,16 @@ use gpui::{
AnyElement, App, AppContext, Entity, IntoElement, ParentElement, SharedString, Styled,
WeakEntity, Window, div, px,
};
use gpui_kit::component::button::{Button, ButtonVariants};
use gpui_kit::component::dialog::DialogButtonProps;
use gpui_kit::component::form::field as form_field;
use gpui_kit::component::input::InputState;
use gpui_kit::component::notification::Notification;
use gpui_kit::component::switch::Switch;
use gpui_kit::component::{ActiveTheme, Sizable, WindowExt, h_flex, v_flex};
use nip29::{Group, GroupId, GroupMetadata};
use nostr_sdk::prelude::RelayUrl;
use theme::{ActiveTheme, TextStyle, Typeset as _};
use ui::button::{Button, ButtonVariants};
use ui::dialog::DialogButtonProps;
use ui::input::InputState;
use ui::notification::Notification;
use ui::switch::Switch;
use ui::{Sizable, WindowExtension, h_flex, v_flex};
use ui::{TextStyle, Typeset as _};
use crate::util::field;
@@ -99,13 +100,22 @@ pub(crate) fn open(group: WeakEntity<Group>, window: &mut Window, cx: &mut App)
let current = *flags.read(cx);
let supported = *livekit.read(cx) != Some(false);
this.confirm()
this.close_button(true)
.title("Edit Group")
.button_props(
DialogButtonProps::default()
.cancel_text("Cancel")
.ok_text("Save"),
)
.footer(
gpui_kit::component::dialog::DialogFooter::new().child(
gpui_kit::component::dialog::DialogAction::new().child(
gpui_kit::component::button::Button::new("save")
.label("Save")
.primary(),
),
),
)
.child(
v_flex()
.gap_3()
@@ -116,58 +126,73 @@ pub(crate) fn open(group: WeakEntity<Group>, window: &mut Window, cx: &mut App)
.child(children_section(&children, cx))
.child(
v_flex()
.gap(px(theme::SPACE))
.gap(px(ui::SPACE))
.child(
Switch::new("private")
form_field()
.label("Private")
.description("Only members can read group messages")
.checked(current.private)
.on_click(toggle(&flags, |flags| &mut flags.private)),
.child(
Switch::new("private")
.checked(current.private)
.on_click(toggle(&flags, |flags| &mut flags.private)),
),
)
.child(
Switch::new("restricted")
form_field()
.label("Restricted")
.description("Only members can post messages")
.checked(current.restricted)
.on_click(toggle(&flags, |flags| &mut flags.restricted)),
.child(
Switch::new("restricted")
.checked(current.restricted)
.on_click(toggle(&flags, |flags| {
&mut flags.restricted
})),
),
)
.child(
Switch::new("hidden")
form_field()
.label("Hidden")
.description("Hidden from non-members, not discoverable")
.checked(current.hidden)
.on_click(toggle(&flags, |flags| &mut flags.hidden)),
.child(
Switch::new("hidden")
.checked(current.hidden)
.on_click(toggle(&flags, |flags| &mut flags.hidden)),
),
)
.child(
Switch::new("closed")
form_field()
.label("Closed")
.description("Join requests are ignored (invite-only)")
.checked(current.closed)
.on_click(toggle(&flags, |flags| &mut flags.closed)),
.child(
Switch::new("closed")
.checked(current.closed)
.on_click(toggle(&flags, |flags| &mut flags.closed)),
),
)
.child(
Switch::new("livekit")
form_field()
.label("LiveKit")
.description(if supported {
"Announce audio and video support."
} else {
"The relay doesn't announce LiveKit support."
})
.checked(current.livekit)
.on_click({
let flags = flags.clone();
let livekit = livekit.clone();
.child(
Switch::new("livekit").checked(current.livekit).on_click({
let flags = flags.clone();
let livekit = livekit.clone();
move |next, _window, cx| {
if *next && *livekit.read(cx) == Some(false) {
return;
move |next, _window, cx| {
if *next && *livekit.read(cx) == Some(false) {
return;
}
flags.update(cx, |flags, cx| {
flags.livekit = *next;
cx.notify();
});
}
flags.update(cx, |flags, cx| {
flags.livekit = *next;
cx.notify();
});
}
}),
}),
),
),
),
)
@@ -263,14 +288,14 @@ fn children_section(children: &Entity<Vec<GroupId>>, cx: &App) -> AnyElement {
let mut section = v_flex()
.gap_1()
.text_style(TextStyle::Callout)
.text_color(cx.theme().text_muted)
.text_color(cx.theme().muted_foreground)
.child("Children");
if list.is_empty() {
return section
.child(
div()
.text_color(cx.theme().text_faint)
.text_color(cx.theme().muted_foreground)
.child("No subgroups"),
)
.into_any_element();
@@ -295,7 +320,7 @@ fn children_section(children: &Entity<Vec<GroupId>>, cx: &App) -> AnyElement {
div()
.min_w_0()
.truncate()
.text_color(cx.theme().text)
.text_color(cx.theme().foreground)
.child(SharedString::from(child.as_str().to_owned())),
)
.child(order),
+6 -6
View File
@@ -1,11 +1,11 @@
use anyhow::Result;
use gpui::{App, Div, Entity, ParentElement, SharedString, Styled, Task, Window};
use gpui_kit::component::input::{Input, InputState};
use gpui_kit::component::notification::Notification;
use gpui_kit::component::{ActiveTheme, WindowExt, v_flex};
use nip29::{Group, Pin, Row};
use nostr_sdk::prelude::Url;
use theme::{ActiveTheme, TextStyle, Typeset as _};
use ui::input::{Input, InputState};
use ui::notification::Notification;
use ui::{WindowExtension, v_flex};
use ui::{TextStyle, Typeset as _};
pub(crate) fn display_name(group: &Group) -> SharedString {
group
@@ -36,7 +36,7 @@ pub(crate) fn opens_run(rows: &[Row], index: usize) -> bool {
};
match (current, previous) {
(Row::Message(current), Row::Message(previous)) => ui::message::opens_run(
(Row::Message(current), Row::Message(previous)) => chat_ui::message::opens_run(
Some((previous.author, previous.created_at.as_secs())),
(current.author, current.created_at.as_secs()),
),
@@ -48,7 +48,7 @@ pub(crate) fn field(label: &'static str, input: &Entity<InputState>, cx: &App) -
v_flex()
.gap_1()
.text_style(TextStyle::Callout)
.text_color(cx.theme().text_muted)
.text_color(cx.theme().muted_foreground)
.child(label)
.child(Input::new(input))
}