This commit is contained in:
2026-10-22 21:07:18 +07:00
parent bd9f4e2861
commit c3c8142f06
24 changed files with 398 additions and 296 deletions
+8 -9
View File
@@ -1,8 +1,6 @@
use std::collections::BTreeMap;
use anyhow::Result;
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,
@@ -26,7 +24,7 @@ use serde::Deserialize;
use settings::AppSettings;
use smallvec::{SmallVec, smallvec};
use state::{NostrRegistry, upload};
use ui::Avatar;
use ui::{Avatar, RenderedText, WelcomeMessage, rendered_text};
use util::{attachment_name, display_name, opens_run, pin_label, report};
mod details;
@@ -987,7 +985,10 @@ impl GroupPanel {
.or_insert_with(|| {
rendered_text(
&message.content,
&message.mentions,
message
.mentions
.iter()
.map(|mention| (mention.range.clone(), mention.public_key)),
&message.emojis,
&directory,
cx,
@@ -1065,7 +1066,6 @@ impl GroupPanel {
.loading(self.uploading)
.disabled(self.uploading)
.ghost()
.large()
.on_click(cx.listener(|this, _event, window, cx| {
this.upload(window, cx);
})),
@@ -1073,10 +1073,9 @@ impl GroupPanel {
.child(Textarea::new(&self.input).appearance(false).flex_1())
.child(
Button::new("send")
.icon(IconName::SendHorizontal)
.icon(ui::IconName::PaperPlaneFill)
.tooltip("Send")
.ghost()
.large()
.on_click(cx.listener(|this, _event, window, cx| {
this.send(window, cx);
})),
@@ -1141,7 +1140,7 @@ impl GroupPanel {
)
.child(
Button::new("cancel-reply")
.icon(IconName::X)
.icon(ui::IconName::Close)
.tooltip("Cancel reply")
.ghost()
.small()
@@ -1178,7 +1177,7 @@ impl GroupPanel {
)
.child(
Button::new(SharedString::from(format!("remove-attachment-{ix}")))
.icon(IconName::X)
.icon(ui::IconName::Close)
.tooltip("Remove")
.ghost()
.xsmall()
+34 -67
View File
@@ -1,5 +1,4 @@
use chat::Message;
use chat_ui::message::{MessageMedia, MessageRow, ReactionBar, ReplyPreview, group_reactions};
use common::TimestampExt;
use gpui::prelude::FluentBuilder;
use gpui::{
@@ -8,19 +7,20 @@ use gpui::{
};
use gpui_kit::assets::IconName;
use gpui_kit::base::StyledExt as _;
use gpui_kit::component::menu::{ContextMenuExt as _, PopupMenu, PopupMenuItem};
use gpui_kit::component::menu::{ContextMenuExt as _, 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 ui::Avatar;
use ui::{
Avatar, MessageMedia, MessageRow, REACTION_EMOJIS, ReactionBar, ReplyPreview, group_reactions,
react_menu,
};
use crate::GroupPanel;
const REACTIONS: [&str; 6] = ["👍", "❤️", "😂", "🎉", "😮", "😢"];
pub(crate) struct Actions {
pub group: WeakEntity<Group>,
pub panel: WeakEntity<GroupPanel>,
@@ -89,6 +89,22 @@ pub(crate) fn render(
let group = actions.group;
let panel = actions.panel;
// Reading the panel here would re-enter its ongoing update, so resolve
// the current user's custom emojis through the shared person directory.
let custom = nostr
.read(cx)
.current_user()
.map(|current_user| {
persons
.read(cx)
.person(&current_user, cx)
.emojis()
.iter()
.map(|(shortcode, _)| format!(":{shortcode}:"))
.collect::<Vec<_>>()
})
.unwrap_or_default();
let row = div()
.id(SharedString::from(format!("message-menu-{}", id.to_hex())))
.w_full()
@@ -107,8 +123,20 @@ pub(crate) fn render(
.submenu("React", window, cx, {
let group = group.clone();
let panel = panel.clone();
let custom = custom.clone();
move |menu, _window, cx| react_menu(menu, group.clone(), panel.clone(), id, cx)
move |menu, _window, _cx| {
let group = group.clone();
let panel = panel.clone();
react_menu(menu, REACTION_EMOJIS, &custom, move |emoji, _window, cx| {
if let Err(error) = panel.update_in(cx, |panel, window, cx| {
panel.react(group.clone(), id, emoji.to_owned(), window, cx);
}) {
log::warn!("nip29: sending a reaction failed: {error}");
}
})
}
});
if !admin {
@@ -143,67 +171,6 @@ pub(crate) fn render(
row.into_any_element()
}
fn react_menu(
menu: PopupMenu,
group: WeakEntity<Group>,
panel: WeakEntity<GroupPanel>,
id: EventId,
cx: &App,
) -> PopupMenu {
let menu = REACTIONS.iter().fold(menu, |menu, emoji| {
let emoji = *emoji;
let group = group.clone();
let panel = panel.clone();
menu.item(
PopupMenuItem::new(emoji).on_click(move |_event, _window, cx| {
if let Err(error) = panel.update_in(cx, |panel, window, cx| {
panel.react(group.clone(), id, emoji.to_owned(), window, cx);
}) {
log::warn!("nip29: sending a reaction failed: {error}");
}
}),
)
});
let emojis = panel
.upgrade()
.and_then(|panel| {
let current_user = NostrRegistry::global(cx).read(cx).current_user()?;
Some(
panel
.read(cx)
.directory
.read(cx)
.person(&current_user, cx)
.emojis()
.to_vec(),
)
})
.unwrap_or_default();
if emojis.is_empty() {
return menu;
}
let menu = menu.separator();
emojis.into_iter().fold(menu, |menu, (shortcode, _)| {
let emoji = format!(":{shortcode}:");
let group = group.clone();
let panel = panel.clone();
menu.item(
PopupMenuItem::new(emoji.clone()).on_click(move |_event, _window, cx| {
if let Err(error) = panel.update_in(cx, |panel, window, cx| {
panel.react(group.clone(), id, emoji.clone(), window, cx);
}) {
log::warn!("nip29: sending a reaction failed: {error}");
}
}),
)
})
}
pub(crate) fn render_activity(
ix: usize,
activity: &Activity,
+1 -1
View File
@@ -35,7 +35,7 @@ pub(crate) fn opens_run(rows: &[Row], index: usize) -> bool {
};
match (current, previous) {
(Row::Message(current), Row::Message(previous)) => chat_ui::message::opens_run(
(Row::Message(current), Row::Message(previous)) => ui::opens_run(
Some((previous.author, previous.created_at.as_secs())),
(current.author, current.created_at.as_secs()),
),