chore: improve message reply in group chat
This commit is contained in:
@@ -1755,9 +1755,7 @@ impl ChatPanel {
|
|||||||
.border_l_2()
|
.border_l_2()
|
||||||
.border_color(cx.theme().element_active)
|
.border_color(cx.theme().element_active)
|
||||||
.child(
|
.child(
|
||||||
div()
|
h_flex()
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.justify_between()
|
.justify_between()
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
|
|||||||
@@ -455,6 +455,19 @@ impl GroupPanel {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn scroll_to(&self, id: &EventId, cx: &App) {
|
||||||
|
let Some(group) = self.group.upgrade() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let rows = group.read(cx).rows();
|
||||||
|
|
||||||
|
if let Some(ix) = rows.iter().position(|row| row.id() == *id) {
|
||||||
|
// The welcome and load-older rows precede the messages.
|
||||||
|
self.list_state.scroll_to_reveal_item(ix + 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn start_reply(&mut self, id: EventId, window: &mut Window, cx: &mut Context<Self>) {
|
fn start_reply(&mut self, id: EventId, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
self.reply_to = Some(id);
|
self.reply_to = Some(id);
|
||||||
self.input.update(cx, |input, cx| {
|
self.input.update(cx, |input, cx| {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use common::TimestampExt;
|
|||||||
use gpui::prelude::FluentBuilder;
|
use gpui::prelude::FluentBuilder;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
AnyElement, App, InteractiveElement, IntoElement, ObjectFit, ParentElement, SharedString,
|
AnyElement, App, InteractiveElement, IntoElement, ObjectFit, ParentElement, SharedString,
|
||||||
SharedUri, Styled, StyledImage, WeakEntity, div, img, px,
|
SharedUri, StatefulInteractiveElement, Styled, StyledImage, WeakEntity, div, img, px,
|
||||||
};
|
};
|
||||||
use nip29::{Activity, Group, Reaction};
|
use nip29::{Activity, Group, Reaction};
|
||||||
use nostr_sdk::prelude::{EventId, Kind, PublicKey};
|
use nostr_sdk::prelude::{EventId, Kind, PublicKey};
|
||||||
@@ -52,8 +52,13 @@ pub(crate) fn render(
|
|||||||
)
|
)
|
||||||
.author(author.name())
|
.author(author.name())
|
||||||
.timestamp(message.created_at.to_human_time())
|
.timestamp(message.created_at.to_human_time())
|
||||||
.when_some(message.replies_to.first().copied(), |this, target| {
|
.when(!message.replies_to.is_empty(), |this| {
|
||||||
this.child(reply_context(target, &actions.group, cx))
|
this.children(replies(
|
||||||
|
&message.replies_to,
|
||||||
|
&actions.group,
|
||||||
|
&actions.panel,
|
||||||
|
cx,
|
||||||
|
))
|
||||||
})
|
})
|
||||||
.child(content)
|
.child(content)
|
||||||
.when(!message.media.is_empty(), |this| {
|
.when(!message.media.is_empty(), |this| {
|
||||||
@@ -258,56 +263,58 @@ fn reaction_bar(reactions: &[Reaction], cx: &App) -> AnyElement {
|
|||||||
.into_any_element()
|
.into_any_element()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reply_context(target: EventId, group: &WeakEntity<Group>, cx: &App) -> AnyElement {
|
fn replies(
|
||||||
let context = group.upgrade().and_then(|entity| {
|
targets: &[EventId],
|
||||||
entity.read_with(cx, |group, cx| {
|
group: &WeakEntity<Group>,
|
||||||
let message = group.message(target)?;
|
panel: &WeakEntity<GroupPanel>,
|
||||||
|
cx: &App,
|
||||||
|
) -> Vec<AnyElement> {
|
||||||
|
let Some(entity) = group.upgrade() else {
|
||||||
|
return Vec::new();
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut items = Vec::with_capacity(targets.len());
|
||||||
|
|
||||||
|
for (ix, id) in targets.iter().enumerate() {
|
||||||
|
let context = entity.read_with(cx, |group, cx| {
|
||||||
|
let message = group.message(*id)?;
|
||||||
let persons = PersonRegistry::global(cx);
|
let persons = PersonRegistry::global(cx);
|
||||||
let author = persons.read(cx).get(&message.author, cx);
|
let author = persons.read(cx).get(&message.author, cx);
|
||||||
let line = message.content.lines().next().unwrap_or_default().trim();
|
|
||||||
|
|
||||||
Some((
|
Some((author.name(), message.preview()))
|
||||||
author.name(),
|
});
|
||||||
SharedString::from(line.chars().take(80).collect::<String>()),
|
|
||||||
))
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
let (author, snippet) = context.unwrap_or_else(|| {
|
let (author, preview) = context.unwrap_or_else(|| {
|
||||||
(
|
(
|
||||||
SharedString::from("Message"),
|
SharedString::from("Message"),
|
||||||
SharedString::from("original not loaded"),
|
SharedString::from("original not loaded"),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
h_flex()
|
let id = *id;
|
||||||
.min_w_0()
|
let panel = panel.clone();
|
||||||
.gap_1()
|
|
||||||
.items_center()
|
items.push(
|
||||||
.child(
|
|
||||||
Icon::new(IconName::Reply)
|
|
||||||
.small()
|
|
||||||
.text_color(cx.theme().text_faint),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
div()
|
div()
|
||||||
.flex_shrink_0()
|
.id(ix)
|
||||||
.max_w(px(120.))
|
.w_full()
|
||||||
.truncate()
|
.px_2()
|
||||||
.font_semibold()
|
.border_l_2()
|
||||||
.text_style(TextStyle::Caption)
|
.border_color(cx.theme().element_active)
|
||||||
.text_color(cx.theme().text_muted)
|
.text_style(TextStyle::Callout)
|
||||||
.child(author),
|
.child(div().font_semibold().child(author))
|
||||||
)
|
.child(div().w_full().text_ellipsis().line_clamp(1).child(preview))
|
||||||
.child(
|
.hover(|this| this.bg(cx.theme().surface_raised))
|
||||||
div()
|
.on_click(move |_event, _window, cx| {
|
||||||
.min_w_0()
|
let _ = panel.update(cx, |panel, cx| {
|
||||||
.truncate()
|
panel.scroll_to(&id, cx);
|
||||||
.text_style(TextStyle::Caption)
|
});
|
||||||
.text_color(cx.theme().text_faint)
|
})
|
||||||
.child(snippet),
|
.into_any_element(),
|
||||||
)
|
);
|
||||||
.into_any_element()
|
}
|
||||||
|
|
||||||
|
items
|
||||||
}
|
}
|
||||||
|
|
||||||
fn media(media: &[SharedUri], cx: &App) -> AnyElement {
|
fn media(media: &[SharedUri], cx: &App) -> AnyElement {
|
||||||
|
|||||||
Reference in New Issue
Block a user