@@ -22,10 +22,10 @@ use matrix_sdk::{
2222use 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
2727use 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 \n Images/Stickers of type {mime:?} are not yet supported. " ) ,
3442+ format ! ( "{body}\n \n Unsupported 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.
38793942impl SmallStateEventContent for EncryptedMessage {
38803943 fn populate_item_content (
0 commit comments