feat: add group reaction reaction and deletion
This commit is contained in:
@@ -105,6 +105,7 @@ pub fn actions(group: WeakEntity<Group>, cx: &App) -> AnyElement {
|
||||
let edit = group.clone();
|
||||
let invite = group.clone();
|
||||
let channel = group.clone();
|
||||
let delete = group.clone();
|
||||
let leave = group;
|
||||
|
||||
h_flex()
|
||||
@@ -161,6 +162,16 @@ pub fn actions(group: WeakEntity<Group>, cx: &App) -> AnyElement {
|
||||
);
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
Button::new("group-delete")
|
||||
.icon(IconName::CloseCircle)
|
||||
.tooltip("Delete group")
|
||||
.ghost()
|
||||
.small()
|
||||
.on_click(move |_event, window, cx| {
|
||||
confirm_delete(delete.clone(), window, cx);
|
||||
}),
|
||||
)
|
||||
})
|
||||
.child(
|
||||
Button::new("leave")
|
||||
@@ -211,6 +222,42 @@ pub fn confirm_leave(group: WeakEntity<Group>, window: &mut Window, cx: &mut App
|
||||
});
|
||||
}
|
||||
|
||||
pub fn confirm_delete(group: WeakEntity<Group>, window: &mut Window, cx: &mut App) {
|
||||
let Some(name) = group.read_with(cx, |group, _cx| group.display_name()).ok() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(key) = group.read_with(cx, |group, _cx| group.key().clone()).ok() else {
|
||||
return;
|
||||
};
|
||||
|
||||
window.open_dialog(cx, move |this, _window, _cx| {
|
||||
let key = key.clone();
|
||||
|
||||
this.confirm()
|
||||
.width(px(360.))
|
||||
.title(format!("Delete {name}?"))
|
||||
.button_props(
|
||||
DialogButtonProps::default()
|
||||
.cancel_text("Cancel")
|
||||
.ok_text("Delete")
|
||||
.ok_variant(ButtonVariant::Danger),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.text_style(TextStyle::Callout)
|
||||
.child("The relay will remove this group for everyone."),
|
||||
)
|
||||
.on_ok(move |_event, window, cx| {
|
||||
let registry = GroupsRegistry::global(cx);
|
||||
let sent = registry.update(cx, |registry, cx| registry.delete_group(&key, cx));
|
||||
report(window, cx, sent);
|
||||
|
||||
true
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/// Remove a member from the group and surface any relay error as a notification.
|
||||
pub fn remove_member(
|
||||
group: WeakEntity<Group>,
|
||||
@@ -823,6 +870,7 @@ impl GroupPanel {
|
||||
message,
|
||||
content,
|
||||
show_author,
|
||||
group.reactions(&message.id),
|
||||
message::Actions {
|
||||
group: self.group.clone(),
|
||||
panel: cx.weak_entity(),
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use chat::Message;
|
||||
use common::TimestampExt;
|
||||
use gpui::prelude::FluentBuilder;
|
||||
@@ -5,11 +7,12 @@ use gpui::{
|
||||
AnyElement, App, InteractiveElement, IntoElement, ObjectFit, ParentElement, SharedString,
|
||||
SharedUri, Styled, StyledImage, WeakEntity, div, img, px,
|
||||
};
|
||||
use nip29::{Activity, Group};
|
||||
use nostr_sdk::prelude::{EventId, Kind};
|
||||
use nip29::{Activity, Group, Reaction};
|
||||
use nostr_sdk::prelude::{EventId, Kind, PublicKey};
|
||||
use person::PersonRegistry;
|
||||
use settings::AppSettings;
|
||||
use theme::{ActiveTheme, TextStyle, Typeset as _, bubble_radius};
|
||||
use state::NostrRegistry;
|
||||
use theme::{ActiveTheme, TextStyle, Typeset as _, bubble_radius, control_radius};
|
||||
use ui::avatar::Avatar;
|
||||
use ui::menu::{ContextMenu, PopupMenu, PopupMenuItem};
|
||||
use ui::message::MessageRow;
|
||||
@@ -31,6 +34,7 @@ pub(crate) fn render(
|
||||
message: &Message,
|
||||
content: AnyElement,
|
||||
show_author: bool,
|
||||
reactions: &[Reaction],
|
||||
actions: Actions,
|
||||
cx: &App,
|
||||
) -> AnyElement {
|
||||
@@ -52,6 +56,9 @@ pub(crate) fn render(
|
||||
.when(!message.media.is_empty(), |this| {
|
||||
this.child(media(&message.media, cx))
|
||||
})
|
||||
.when(!reactions.is_empty(), |this| {
|
||||
this.child(reaction_bar(reactions, cx))
|
||||
})
|
||||
.into_any_element();
|
||||
|
||||
let id = message.id;
|
||||
@@ -205,6 +212,43 @@ fn action(activity: &Activity) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
fn reaction_bar(reactions: &[Reaction], cx: &App) -> AnyElement {
|
||||
let me = NostrRegistry::global(cx).read(cx).current_user();
|
||||
|
||||
let mut grouped: BTreeMap<String, Vec<PublicKey>> = BTreeMap::new();
|
||||
|
||||
for reaction in reactions {
|
||||
grouped
|
||||
.entry(reaction.emoji.clone())
|
||||
.or_default()
|
||||
.push(reaction.author);
|
||||
}
|
||||
|
||||
h_flex()
|
||||
.mt_1()
|
||||
.gap_1()
|
||||
.children(grouped.into_iter().map(|(emoji, authors)| {
|
||||
let mine = me.is_some_and(|me| authors.contains(&me));
|
||||
|
||||
h_flex()
|
||||
.gap(px(theme::SPACE))
|
||||
.py_0p5()
|
||||
.px_1()
|
||||
.rounded(px(control_radius()))
|
||||
.text_style(TextStyle::Caption)
|
||||
.border_1()
|
||||
.when(mine, |this| {
|
||||
this.text_color(cx.theme().text)
|
||||
.bg(cx.theme().surface_raised)
|
||||
.border_color(cx.theme().element_active)
|
||||
})
|
||||
.when(!mine, |this| this.border_color(cx.theme().border))
|
||||
.child(SharedString::from(emoji))
|
||||
.child(SharedString::from(authors.len().to_string()))
|
||||
}))
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn media(media: &[SharedUri], cx: &App) -> AnyElement {
|
||||
if let [only] = media {
|
||||
return div()
|
||||
|
||||
Reference in New Issue
Block a user