diff --git a/src/session/view/content/room_history/mod.ui b/src/session/view/content/room_history/mod.ui
index b10c3bfe..f7aadd50 100644
--- a/src/session/view/content/room_history/mod.ui
+++ b/src/session/view/content/room_history/mod.ui
@@ -93,6 +93,11 @@
sender-avatar.unban
action-disabled
+ -
+ _Remove Messages
+ sender-avatar.remove-messages
+ action-disabled
+
-
I_gnore
sender-avatar.ignore
diff --git a/src/session/view/content/room_history/sender_avatar/mod.rs b/src/session/view/content/room_history/sender_avatar/mod.rs
index 74716f34..062edfbf 100644
--- a/src/session/view/content/room_history/sender_avatar/mod.rs
+++ b/src/session/view/content/room_history/sender_avatar/mod.rs
@@ -5,7 +5,7 @@ use ruma::events::room::power_levels::PowerLevelUserAction;
use crate::{
components::{Avatar, UserProfileDialog},
- gettext_f,
+ gettext_f, ngettext_f,
prelude::*,
session::{
model::{Member, Membership, User},
@@ -131,6 +131,14 @@ mod imp {
widget.unban().await;
});
+ klass.install_action_async(
+ "sender-avatar.remove-messages",
+ None,
+ |widget, _, _| async move {
+ widget.remove_messages().await;
+ },
+ );
+
klass.install_action_async("sender-avatar.ignore", None, |widget, _, _| async move {
widget.toggle_ignored().await;
});
@@ -313,6 +321,11 @@ mod imp {
&& permissions.can_do_to_user(sender_id, PowerLevelUserAction::Unban),
);
+ obj.action_set_enabled(
+ "sender-avatar.remove-messages",
+ !is_own_user && permissions.can_redact_other(),
+ );
+
obj.action_set_enabled("sender-avatar.ignore", !is_own_user && !sender.is_ignored());
obj.action_set_enabled(
@@ -579,6 +592,59 @@ impl SenderAvatar {
}
}
+ /// Remove the known events of the room member.
+ async fn remove_messages(&self) {
+ let Some(sender) = self.sender() else {
+ return;
+ };
+ let Some(window) = self.root().and_downcast::() else {
+ return;
+ };
+
+ let redactable_events = sender.redactable_events();
+ let count = redactable_events.len();
+
+ let (confirmed, reason) = confirm_room_member_destructive_action(
+ &sender,
+ RoomMemberDestructiveAction::RemoveMessages(count),
+ &window,
+ )
+ .await;
+ if !confirmed {
+ return;
+ }
+
+ let n = u32::try_from(count).unwrap_or(u32::MAX);
+ toast!(
+ self,
+ ngettext_f(
+ // Translators: Do NOT translate the content between '{' and '}',
+ // this is a variable name.
+ "Removing 1 message sent by the user…",
+ "Removing {n} messages sent by the user…",
+ n,
+ &[("n", &n.to_string())]
+ )
+ );
+
+ let room = sender.room();
+
+ if let Err(events) = room.redact(&redactable_events, reason).await {
+ let n = u32::try_from(events.len()).unwrap_or(u32::MAX);
+ toast!(
+ self,
+ ngettext_f(
+ // Translators: Do NOT translate the content between '{' and '}',
+ // this is a variable name.
+ "Failed to remove 1 message sent by the user",
+ "Failed to remove {n} messages sent by the user",
+ n,
+ &[("n", &n.to_string())]
+ )
+ );
+ }
+ }
+
/// Toggle whether the user is ignored or not.
async fn toggle_ignored(&self) {
let Some(sender) = self.sender().and_upcast::() else {