Skip to content

Commit 044c0ab

Browse files
authored
Merge pull request #662 from kevinaboos/show_redactions_as_regular_messages
2 parents 5e54dd0 + ba72432 commit 044c0ab

13 files changed

Lines changed: 234 additions & 156 deletions

src/event_preview.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,6 @@ pub fn text_preview_of_redacted_message(
334334
if redactor == sender_user_id {
335335
format!("{} deleted their own message: \"{}\".", original_sender_username, reason)
336336
} else {
337-
// TODO: get the redactor's display name if possible
338337
format!("{} deleted {}'s message: \"{}\".", redactor, original_sender_username, reason)
339338
}
340339
}
@@ -352,6 +351,7 @@ pub fn text_preview_of_redacted_message(
352351
TextPreview::from((text, BeforeText::Nothing))
353352
}
354353

354+
355355
/// Returns a plaintext preview of the given encrypted message that could not be decrypted.
356356
///
357357
/// This is used for "Unable to decrypt" messages, which may have a known cause

src/home/add_room.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,15 +170,16 @@ live_design! {
170170
width: 45, height: 45,
171171
cursor: Default,
172172
text_view = { text = { draw_text: {
173-
text_style: <TITLE_TEXT>{ font_size: 15.0 }
173+
text_style: <TITLE_TEXT>{ font_size: 16.0 }
174174
}}}
175175
}
176176

177177
room_name = <Label> {
178178
width: Fill, height: Fit,
179+
margin: {top: 3} // align it with the above room_avatar
179180
flow: RightWrap,
180181
draw_text: {
181-
text_style: <TITLE_TEXT>{ font_size: 15 }
182+
text_style: <TITLE_TEXT>{ font_size: 16 }
182183
color: (COLOR_TEXT)
183184
wrap: Word,
184185
}

src/home/event_reaction_list.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::home::room_screen::RoomScreenTooltipActions;
2-
use crate::profile::user_profile_cache::get_user_profile_and_room_member;
2+
use crate::profile::user_profile_cache;
33
use crate::sliding_sync::{current_user_id, submit_async_request, MatrixRequest};
44
use indexmap::IndexMap;
55
use makepad_widgets::*;
@@ -279,8 +279,8 @@ impl ReactionListRef {
279279
if sender == &client_user_id {
280280
includes_user = true;
281281
}
282-
// Cache the reaction sender's user profile so that tooltip will show displayable name
283-
let _ = get_user_profile_and_room_member(cx, sender.clone(), &room_id, true);
282+
// Prefill each reactor's user profile into the cache so the tooltip will show their display name.
283+
let _ = user_profile_cache::with_user_profile(cx, sender.clone(), Some(&room_id), true, |_, _| { });
284284
}
285285

286286
let reaction_data = ReactionData {

src/home/navigation_tab_bar.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,7 @@ pub fn get_own_profile(cx: &mut Cx) -> Option<UserProfile> {
542542
let avatar_uri_to_fetch = user_profile_cache::with_user_profile(
543543
cx,
544544
own_user_id,
545+
None,
545546
true,
546547
|new_profile, _rooms| {
547548
let avatar_uri_to_fetch = new_profile.avatar_state.uri().cloned();

src/home/room_read_receipt.rs

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
use crate::home::room_screen::RoomScreenTooltipActions;
2-
use crate::profile::user_profile_cache::get_user_profile_and_room_member;
2+
use crate::profile::user_profile_cache::get_user_display_name_for_room;
33
use crate::shared::avatar::{AvatarRef, AvatarWidgetRefExt};
44
use crate::utils::human_readable_list;
55
use indexmap::IndexMap;
66
use makepad_widgets::*;
7-
use matrix_sdk::ruma::{events::receipt::Receipt, EventId, OwnedUserId, RoomId};
7+
use matrix_sdk::ruma::{events::receipt::Receipt, EventId, OwnedUserId, OwnedRoomId, RoomId};
88
use matrix_sdk_ui::timeline::EventTimelineItem;
99

1010
use std::cmp;
1111

1212

13-
1413
/// The maximum number of items to display in the read receipts AvatarRow
1514
/// and its accompanying tooltip.
1615
pub const MAX_VISIBLE_AVATARS_IN_READ_RECEIPT: usize = 3;
@@ -255,20 +254,16 @@ pub fn populate_read_receipts(
255254
pub fn populate_tooltip(
256255
cx: &mut Cx,
257256
read_receipts: IndexMap<OwnedUserId, Receipt>,
258-
room_id: &RoomId,
257+
room_id: &OwnedRoomId,
259258
) -> String {
260259
let mut display_names: Vec<String> = read_receipts
261260
.iter()
262261
.rev()
263262
.take(MAX_VISIBLE_AVATARS_IN_READ_RECEIPT)
264263
.map(|(user_id, _)| {
265-
if let (Some(profile), _) =
266-
get_user_profile_and_room_member(cx, user_id.clone(), room_id, true)
267-
{
268-
profile.displayable_name().to_owned()
269-
} else {
270-
user_id.to_string()
271-
}
264+
get_user_display_name_for_room(cx, user_id.clone(), Some(room_id), true)
265+
.into_option()
266+
.unwrap_or_else(|| user_id.to_string())
272267
})
273268
.collect();
274269
for _ in display_names.len()..read_receipts.len() {

src/home/room_screen.rs

Lines changed: 110 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ use matrix_sdk::{
2222
use matrix_sdk_ui::timeline::{
2323
self, EmbeddedEvent, EncryptedMessage, EventTimelineItem, InReplyToDetails, MemberProfileChange, MembershipChange, MsgLikeContent, MsgLikeKind, OtherMessageLike, PollState, RoomMembershipChange, TimelineDetails, TimelineEventItemId, TimelineItem, TimelineItemContent, TimelineItemKind, VirtualTimelineItem
2424
};
25-
use ruma::OwnedUserId;
25+
use ruma::{OwnedUserId, events::{AnySyncMessageLikeEvent, AnySyncTimelineEvent, SyncMessageLikeEvent}};
2626

2727
use crate::{
28-
app::AppStateAction, avatar_cache, event_preview::{plaintext_body_of_timeline_item, text_preview_of_encrypted_message, text_preview_of_member_profile_change, text_preview_of_other_message_like, text_preview_of_other_state, text_preview_of_redacted_message, text_preview_of_room_membership_change, text_preview_of_timeline_item}, home::{edited_indicator::EditedIndicatorWidgetRefExt, link_preview::{LinkPreviewCache, LinkPreviewRef, LinkPreviewWidgetRefExt}, loading_pane::{LoadingPaneState, LoadingPaneWidgetExt}, room_image_viewer::{get_image_name_and_filesize, populate_matrix_image_modal}, rooms_list::RoomsListRef, tombstone_footer::SuccessorRoomDetails}, media_cache::{MediaCache, MediaCacheEntry}, profile::{
28+
app::AppStateAction, avatar_cache, event_preview::{plaintext_body_of_timeline_item, text_preview_of_encrypted_message, text_preview_of_member_profile_change, text_preview_of_other_message_like, text_preview_of_other_state, text_preview_of_room_membership_change, text_preview_of_timeline_item}, home::{edited_indicator::EditedIndicatorWidgetRefExt, link_preview::{LinkPreviewCache, LinkPreviewRef, LinkPreviewWidgetRefExt}, loading_pane::{LoadingPaneState, LoadingPaneWidgetExt}, room_image_viewer::{get_image_name_and_filesize, populate_matrix_image_modal}, rooms_list::RoomsListRef, tombstone_footer::SuccessorRoomDetails}, media_cache::{MediaCache, MediaCacheEntry}, profile::{
2929
user_profile::{AvatarState, ShowUserProfileAction, UserProfile, UserProfileAndRoomId, UserProfilePaneInfo, UserProfileSlidingPaneRef, UserProfileSlidingPaneWidgetExt},
3030
user_profile_cache,
3131
},
@@ -201,6 +201,7 @@ live_design! {
201201
edited_indicator = <EditedIndicator> { }
202202
tsp_sign_indicator = <TspSignIndicator> { }
203203
}
204+
204205
content = <View> {
205206
width: Fill,
206207
height: Fit
@@ -236,7 +237,6 @@ live_design! {
236237
reaction_list = <ReactionList> { }
237238
avatar_row = <AvatarRow> {}
238239
}
239-
240240
}
241241
}
242242
}
@@ -632,11 +632,20 @@ impl Widget for RoomScreen {
632632
reaction_data,
633633
} = reaction_list.hovered_in(actions) {
634634
let Some(_tl_state) = self.tl_state.as_ref() else { continue };
635-
let tooltip_text_arr: Vec<String> = reaction_data.reaction_senders.iter().map(|(sender, _react_info)| {
636-
user_profile_cache::get_user_profile_and_room_member(cx, sender.clone(), &reaction_data.room_id, true).0
637-
.map(|user_profile| user_profile.displayable_name().to_string())
635+
let tooltip_text_arr: Vec<String> = reaction_data.reaction_senders
636+
.iter()
637+
.map(|(sender, _react_info)| {
638+
user_profile_cache::get_user_display_name_for_room(
639+
cx,
640+
sender.clone(),
641+
Some(&reaction_data.room_id),
642+
true,
643+
)
644+
.into_option()
638645
.unwrap_or_else(|| sender.to_string())
639-
}).collect();
646+
})
647+
.collect();
648+
640649
let mut tooltip_text = utils::human_readable_list(&tooltip_text_arr, MAX_VISIBLE_AVATARS_IN_READ_RECEIPT);
641650
tooltip_text.push_str(&format!(" reacted with: {}", reaction_data.reaction));
642651
cx.widget_action(
@@ -1024,7 +1033,9 @@ impl Widget for RoomScreen {
10241033
let (item, item_new_draw_status) = match timeline_item.kind() {
10251034
TimelineItemKind::Event(event_tl_item) => match event_tl_item.content() {
10261035
TimelineItemContent::MsgLike(msg_like_content) => match &msg_like_content.kind {
1027-
MsgLikeKind::Message(_) | MsgLikeKind::Sticker(_) => {
1036+
MsgLikeKind::Message(_)
1037+
| MsgLikeKind::Sticker(_)
1038+
| MsgLikeKind::Redacted => {
10281039
let prev_event = tl_idx.checked_sub(1).and_then(|i| tl_items.get(i));
10291040
populate_message_view(
10301041
cx,
@@ -1052,15 +1063,6 @@ impl Widget for RoomScreen {
10521063
poll_state,
10531064
item_drawn_status,
10541065
),
1055-
MsgLikeKind::Redacted => populate_small_state_event(
1056-
cx,
1057-
list,
1058-
item_id,
1059-
room_id,
1060-
event_tl_item,
1061-
&RedactedMessageEventMarker,
1062-
item_drawn_status,
1063-
),
10641066
MsgLikeKind::UnableToDecrypt(utd) => populate_small_state_event(
10651067
cx,
10661068
list,
@@ -2841,7 +2843,7 @@ fn populate_message_view(
28412843
MsgLikeKind::Message(msg) => {
28422844
match msg.msgtype() {
28432845
MessageType::Text(TextMessageEventContent { body, formatted, .. }) => {
2844-
has_html_body = formatted.as_ref().is_some_and(|f| f.format == MessageFormat::Html);
2846+
has_html_body = formatted.as_ref().is_some_and(|f| f.format == MessageFormat::Html);
28452847
let template = if use_compact_view {
28462848
id!(CondensedMessage)
28472849
} else {
@@ -3177,6 +3179,35 @@ fn populate_message_view(
31773179
(item, true)
31783180
}
31793181
}
3182+
}
3183+
// Handle messages that have been redacted (deleted).
3184+
MsgLikeKind::Redacted => {
3185+
has_html_body = false;
3186+
let template = if use_compact_view {
3187+
id!(CondensedMessage)
3188+
} else {
3189+
id!(Message)
3190+
};
3191+
let (item, existed) = list.item_with_existed(cx, item_id, template);
3192+
if existed && item_drawn_status.content_drawn {
3193+
(item, true)
3194+
} else {
3195+
let html_or_plaintext_ref = item.html_or_plaintext(ids!(content.message));
3196+
html_or_plaintext_ref.apply_over(cx, live!{
3197+
html_view = { html = {
3198+
font_size: (REDACTED_MESSAGE_FONT_SIZE),
3199+
draw_normal: { text_style: { font_size: (REDACTED_MESSAGE_FONT_SIZE)} }
3200+
draw_italic: { text_style: { font_size: (REDACTED_MESSAGE_FONT_SIZE)} }
3201+
} }
3202+
});
3203+
new_drawn_status.content_drawn = populate_redacted_message_content(
3204+
cx,
3205+
&html_or_plaintext_ref,
3206+
event_tl_item,
3207+
room_id,
3208+
);
3209+
(item, false)
3210+
}
31803211
}
31813212
other => {
31823213
has_html_body = false;
@@ -3408,7 +3439,7 @@ fn populate_image_message_content(
34083439
if ImageFormat::from_mimetype(mime).is_none() {
34093440
text_or_image_ref.show_text(
34103441
cx,
3411-
format!("{body}\n\nImages/Stickers of type {mime:?} are not yet supported."),
3442+
format!("{body}\n\nUnsupported type {mime:?}"),
34123443
);
34133444
return true; // consider this as fully drawn
34143445
}
@@ -3688,6 +3719,66 @@ fn populate_location_message_content(
36883719
}
36893720

36903721

3722+
/// Draws the given redacted message's content into the `message_content_widget`.
3723+
///
3724+
/// Returns whether the redacted message content was fully drawn.
3725+
fn populate_redacted_message_content(
3726+
cx: &mut Cx,
3727+
message_content_widget: &HtmlOrPlaintextRef,
3728+
event_tl_item: &EventTimelineItem,
3729+
room_id: &OwnedRoomId,
3730+
) -> bool {
3731+
let fully_drawn: bool;
3732+
let mut redactor_id_and_reason = None;
3733+
if let Some(redacted_msg) = event_tl_item.latest_json() {
3734+
if let Ok(AnySyncTimelineEvent::MessageLike(
3735+
AnySyncMessageLikeEvent::RoomMessage(
3736+
SyncMessageLikeEvent::Redacted(redaction)
3737+
)
3738+
)) = redacted_msg.deserialize() {
3739+
if let Ok(redacted_because) = redaction.unsigned.redacted_because.deserialize() {
3740+
redactor_id_and_reason = Some((
3741+
redacted_because.sender,
3742+
redacted_because.content.reason,
3743+
));
3744+
}
3745+
}
3746+
}
3747+
3748+
let html = if let Some((redactor, reason)) = redactor_id_and_reason {
3749+
if redactor == event_tl_item.sender() {
3750+
fully_drawn = true;
3751+
match reason {
3752+
Some(r) => format!("⛔ <i>Deleted their own message. Reason: \"{}\".</i>", htmlize::escape_text(r)),
3753+
None => String::from("⛔ <i>Deleted their own message.</i>"),
3754+
}
3755+
} else {
3756+
// Try to get the displayable name of the user who redacted this message.
3757+
let redactor_name = user_profile_cache::get_user_display_name_for_room(
3758+
cx,
3759+
redactor.clone(),
3760+
Some(room_id),
3761+
true,
3762+
);
3763+
fully_drawn = redactor_name.was_found();
3764+
let redactor_name_esc = htmlize::escape_text(redactor_name.as_deref().unwrap_or(redactor.as_str()));
3765+
match reason {
3766+
Some(r) => format!("⛔ <i>{} deleted this message. Reason: \"{}\".</i>",
3767+
redactor_name_esc,
3768+
htmlize::escape_text(r),
3769+
),
3770+
None => format!("⛔ <i>{} deleted this message.</i>", redactor_name_esc),
3771+
}
3772+
}
3773+
} else {
3774+
fully_drawn = true;
3775+
String::from("⛔ <i>Message deleted.</i>")
3776+
};
3777+
message_content_widget.show_html(cx, html);
3778+
fully_drawn
3779+
}
3780+
3781+
36913782
/// Draws a ReplyPreview above a message if it was in-reply to another message.
36923783
///
36933784
/// ## Arguments
@@ -3847,34 +3938,6 @@ trait SmallStateEventContent {
38473938
) -> (WidgetRef, ItemDrawnStatus);
38483939
}
38493940

3850-
/// An empty marker struct used for populating redacted messages.
3851-
struct RedactedMessageEventMarker;
3852-
3853-
impl SmallStateEventContent for RedactedMessageEventMarker {
3854-
fn populate_item_content(
3855-
&self,
3856-
cx: &mut Cx,
3857-
_list: &mut PortalList,
3858-
_item_id: usize,
3859-
item: WidgetRef,
3860-
event_tl_item: &EventTimelineItem,
3861-
original_sender: &str,
3862-
_item_drawn_status: ItemDrawnStatus,
3863-
mut new_drawn_status: ItemDrawnStatus,
3864-
) -> (WidgetRef, ItemDrawnStatus) {
3865-
item.label(ids!(content)).set_text(
3866-
cx,
3867-
&text_preview_of_redacted_message(
3868-
event_tl_item.latest_json(),
3869-
event_tl_item.sender(),
3870-
original_sender,
3871-
).format_with(original_sender, false),
3872-
);
3873-
new_drawn_status.content_drawn = true;
3874-
(item, new_drawn_status)
3875-
}
3876-
}
3877-
38783941
// For unable to decrypt messages.
38793942
impl SmallStateEventContent for EncryptedMessage {
38803943
fn populate_item_content(

src/home/rooms_list_entry.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ live_design! {
5858
font_size: 7.5
5959
},
6060
}
61-
text: "??"
6261
}
6362

6463
MessagePreview = <View> {

0 commit comments

Comments
 (0)