This commit is contained in:
2026-11-11 18:46:45 +07:00
parent 2f9559228f
commit 9a7afbe3e2
26 changed files with 212 additions and 193 deletions
+4 -3
View File
@@ -1,5 +1,6 @@
use gpui::{
AnyElement, App, IntoElement, ParentElement, SharedString, Styled, WeakEntity, Window, div, px,
AnyElement, App, IntoElement, ParentElement, SharedString, Styled, WeakEntity, Window, div,
rems,
};
use gpui_kit::base::StyledExt as _;
use gpui_kit::component::description_list::{DescriptionItem, DescriptionList};
@@ -10,9 +11,9 @@ use ui::Avatar;
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.))
this.max_h(rems(37.5))
.close_button(true)
.title("About Group")
.title("About group")
.child(body(&group, cx))
});
}
+2 -2
View File
@@ -3,7 +3,7 @@ use common::TimestampExt;
use gpui::prelude::FluentBuilder;
use gpui::{
App, AppContext, ClipboardItem, Entity, IntoElement, ParentElement, SharedString, Styled,
WeakEntity, Window, div, px,
WeakEntity, Window, div, px, rems,
};
use gpui_kit::component::button::{Button, ButtonVariant, ButtonVariants};
use gpui_kit::component::dialog::DialogButtonProps;
@@ -25,7 +25,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.))
this.max_h(rems(37.5))
.close_button(true)
.title("Invites")
.child(body(group.clone(), input.clone(), cx))
+18 -20
View File
@@ -8,7 +8,7 @@ use gpui::{
Action, Anchor, AnyElement, App, AppContext, ClipboardItem, Context, Entity, EventEmitter,
FocusHandle, Focusable, FollowMode, InteractiveElement, IntoElement, ListAlignment,
ListScrollEvent, ListState, ParentElement, Render, SharedString, Styled, Subscription, Task,
WeakEntity, Window, div, list, px,
WeakEntity, Window, div, list, px, rems,
};
use gpui_kit::assets::IconName;
use gpui_kit::base::StyledExt as _;
@@ -167,7 +167,7 @@ pub fn actions(group: WeakEntity<Group>, cx: &App) -> AnyElement {
)
.child(
Button::new("group-delete")
.icon(IconName::CircleX)
.icon(IconName::Trash)
.tooltip("Delete group")
.ghost()
.small()
@@ -965,16 +965,11 @@ impl GroupPanel {
.py_1p5()
.border_b_1()
.border_color(cx.theme().border)
.children(pins.iter().map(|pin| {
div()
.px_1p5()
.py_0p5()
.rounded(cx.theme().radius)
.bg(cx.theme().primary)
.text_xs()
.text_color(cx.theme().muted_foreground)
.child(pin_label(group, pin))
}))
.children(
pins.iter().map(|pin| {
gpui_kit::component::tag::Tag::secondary().child(pin_label(group, pin))
}),
)
.into_any_element()
}
@@ -1025,7 +1020,7 @@ impl GroupPanel {
.child(
Button::new("load-older")
.label(if loading {
"Loading earlier messages…"
"Loading earlier messages"
} else {
"Load earlier messages"
})
@@ -1083,7 +1078,7 @@ impl GroupPanel {
row.child("You're in preview mode. Join this group to talk.")
.child(
Button::new("join")
.label("Request to Join")
.label("Request to join")
.small()
.ghost()
.on_click(cx.listener(move |this, _event, _window, cx| {
@@ -1152,7 +1147,11 @@ impl GroupPanel {
cx,
)
})
.element(ix.into(), window, cx),
.element(
SharedString::from(format!("text-{}", message.id.to_hex())).into(),
window,
cx,
),
};
let admin = nostr
@@ -1378,7 +1377,7 @@ impl GroupPanel {
.child(
div()
.flex_shrink_0()
.max_w(px(140.))
.max_w(rems(8.75))
.truncate()
.font_semibold()
.text_color(cx.theme().foreground)
@@ -1408,8 +1407,7 @@ impl GroupPanel {
fn render_attachments(&self, cx: &mut Context<Self>) -> Vec<AnyElement> {
self.attachments
.iter()
.enumerate()
.map(|(ix, url)| {
.map(|url| {
let url = url.clone();
h_flex()
@@ -1425,12 +1423,12 @@ impl GroupPanel {
.child(Icon::new(IconName::Link).xsmall())
.child(
div()
.max_w(px(200.))
.max_w(rems(12.5))
.truncate()
.child(attachment_name(&url)),
)
.child(
Button::new(SharedString::from(format!("remove-attachment-{ix}")))
Button::new(SharedString::from(format!("remove-attachment-{}", url)))
.icon(IconName::Close)
.tooltip("Remove")
.ghost()
+50 -41
View File
@@ -33,7 +33,7 @@ pub(crate) struct Actions {
}
pub(crate) fn render(
ix: usize,
_ix: usize,
message: &Message,
content: AnyElement,
show_author: bool,
@@ -58,33 +58,36 @@ pub(crate) fn render(
nostr.read(cx).current_user(),
);
let row = MessageRow::new(ix)
.show_author(show_author)
.hide_avatar(hide_avatar)
.avatar(
Avatar::new(author.avatar())
.seed(author.avatar_seed())
.small()
.flex_shrink_0(),
)
.author(author.name())
.timestamp(message.created_at.to_human_time())
.when(!message.replies_to.is_empty(), |this| {
this.children(replies(
&message.replies_to,
&persons,
&actions.group,
&actions.panel,
cx,
))
})
.child(content)
.when(!message.media.is_empty(), |this| {
this.child(MessageMedia::new(&message.media))
})
.when(!reaction_groups.is_empty(), |this| {
this.child(ReactionBar::new(reaction_groups))
});
let row = MessageRow::new(SharedString::from(format!(
"message-{}",
message.id.to_hex()
)))
.show_author(show_author)
.hide_avatar(hide_avatar)
.avatar(
Avatar::new(author.avatar())
.seed(author.avatar_seed())
.small()
.flex_shrink_0(),
)
.author(author.name())
.timestamp(message.created_at.to_human_time())
.when(!message.replies_to.is_empty(), |this| {
this.children(replies(
&message.replies_to,
&persons,
&actions.group,
&actions.panel,
cx,
))
})
.child(content)
.when(!message.media.is_empty(), |this| {
this.child(MessageMedia::new(&message.media))
})
.when(!reaction_groups.is_empty(), |this| {
this.child(ReactionBar::new(reaction_groups))
});
let id = message.id;
let admin = actions.admin;
@@ -249,7 +252,7 @@ pub(crate) fn render(
}
pub(crate) fn render_activity(
ix: usize,
_ix: usize,
activity: &Activity,
persons: &Entity<Directory>,
cx: &App,
@@ -258,12 +261,14 @@ pub(crate) fn render_activity(
let hide_avatar = AppSettings::get_hide_avatar(cx);
h_flex()
.id(ix)
.id(SharedString::from(format!(
"activity-{}",
activity.id.to_hex()
)))
.w_full()
.gap_2()
.py_1()
.px_3()
.hover(|this| this.bg(cx.theme().list_hover))
.child(badge(activity, cx))
.when(!hide_avatar, |this| {
this.child(
@@ -340,7 +345,7 @@ fn replies(
let mut items = Vec::with_capacity(targets.len());
for (ix, id) in targets.iter().enumerate() {
for id in targets.iter() {
let context = entity.read_with(cx, |group, cx| {
let message = group.message(*id)?;
let author = persons.read(cx).person(&message.author, cx);
@@ -359,15 +364,19 @@ fn replies(
let panel = panel.clone();
items.push(
ReplyPreview::new(ix, author, preview)
.on_click(move |_event, _window, cx| {
if let Err(error) = panel.update(cx, |panel, cx| {
panel.scroll_to(&id, cx);
}) {
log::warn!("nip29: scrolling to message failed: {error}");
}
})
.into_any_element(),
ReplyPreview::new(
SharedString::from(format!("reply-{}", id.to_hex())),
author,
preview,
)
.on_click(move |_event, _window, cx| {
if let Err(error) = panel.update(cx, |panel, cx| {
panel.scroll_to(&id, cx);
}) {
log::warn!("nip29: scrolling to message failed: {error}");
}
})
.into_any_element(),
);
}
+22 -18
View File
@@ -11,7 +11,7 @@ use gpui_kit::component::dialog::DialogButtonProps;
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 gpui_kit::component::{ActiveTheme, Disableable, Sizable, WindowExt, h_flex, v_flex};
use nip29::{Group, GroupId, GroupMetadata};
use nostr_sdk::prelude::RelayUrl;
use ui::settings_row;
@@ -100,7 +100,7 @@ pub(crate) fn open(group: WeakEntity<Group>, window: &mut Window, cx: &mut App)
let supported = *livekit.read(cx) != Some(false);
this.close_button(true)
.title("Edit Group")
.title("Edit group")
.button_props(
DialogButtonProps::default()
.cancel_text("Cancel")
@@ -161,24 +161,27 @@ pub(crate) fn open(group: WeakEntity<Group>, window: &mut Window, cx: &mut App)
.child(settings_row(
"LiveKit",
if supported {
"Announce audio and video support."
"Announce audio and video support"
} else {
"The relay doesn't announce LiveKit support."
},
Switch::new("livekit").checked(current.livekit).on_click({
let flags = flags.clone();
let livekit = livekit.clone();
Switch::new("livekit")
.checked(current.livekit)
.disabled(!supported)
.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();
});
}
}),
}),
cx,
)),
),
@@ -293,10 +296,10 @@ fn children_section(children: &Entity<Vec<GroupId>>, cx: &App) -> AnyElement {
.flex_shrink_0()
.gap_1()
.when(ix > 0, |this| {
this.child(move_button(children.clone(), ix, ix - 1, "up"))
this.child(move_button(children.clone(), child, ix, ix - 1, "up"))
})
.when(ix + 1 < list.len(), |this| {
this.child(move_button(children.clone(), ix, ix + 1, "down"))
this.child(move_button(children.clone(), child, ix, ix + 1, "down"))
});
section = section.child(
@@ -319,11 +322,12 @@ fn children_section(children: &Entity<Vec<GroupId>>, cx: &App) -> AnyElement {
fn move_button(
children: Entity<Vec<GroupId>>,
child: &GroupId,
ix: usize,
target: usize,
direction: &'static str,
) -> Button {
Button::new(SharedString::from(format!("child-{direction}-{ix}")))
Button::new(SharedString::from(format!("child-{direction}-{child}")))
.label(if direction == "up" { "Up" } else { "Down" })
.ghost()
.small()