@@ -5,7 +5,7 @@ use makepad_widgets::*;
5
5
use matrix_sdk_ui:: timeline:: { AnyOtherFullStateEventContent , InReplyToDetails , Profile , ReactionsByKeyBySender , TimelineDetails , TimelineEventItemId } ;
6
6
use ruma:: { events:: { receipt:: Receipt , room:: message:: { AudioMessageEventContent , EmoteMessageEventContent , FileMessageEventContent , FormattedBody , ImageMessageEventContent , KeyVerificationRequestEventContent , MessageType , NoticeMessageEventContent , Relation , RoomMessageEventContent , TextMessageEventContent , VideoMessageEventContent } , AnyMessageLikeEvent , AnyMessageLikeEventContent , AnyStateEventContent , AnyTimelineEvent , FullStateEventContent } , uint, EventId , MilliSecondsSinceUnixEpoch , OwnedRoomId , OwnedUserId , UserId } ;
7
7
8
- use crate :: { event_preview:: text_preview_of_other_state, shared:: room_filter_input_bar:: RoomFilterAction , sliding_sync:: { current_user_id, submit_async_request, MatrixRequest } , utils:: unix_time_millis_to_datetime} ;
8
+ use crate :: { app :: AppState , event_preview:: text_preview_of_other_state, shared:: room_filter_input_bar:: RoomFilterAction , sliding_sync:: { current_user_id, submit_async_request, MatrixRequest } , utils:: unix_time_millis_to_datetime} ;
9
9
10
10
use crate :: home:: { new_message_context_menu:: MessageDetails , room_screen:: { populate_message_view, populate_small_state_event, Eventable , ItemDrawnStatus , MessageAction , MessageOrSticker , MsgTypeAble , PreviousEventable , RoomScreen , SmallStateEventContent , TimelineUiState } , rooms_list:: RoomsListWidgetExt } ;
11
11
@@ -270,6 +270,13 @@ impl SearchResultRef {
270
270
}
271
271
}
272
272
273
+ /// This is a specialized version of `RoomScreen::draw_walk()` that is specific to rendering the timeline of a search result.
274
+ ///
275
+ /// It takes a `RoomScreen` widget and iterates through the `PortalList` widget inside it. For each item in the list, it checks if the item is a `DateDivider`, `ContextEvent` or `Event` and renders it accordingly.
276
+ ///
277
+ /// The rendering of the timeline items is done by calling `populate_message_view()` for messages and `populate_small_state_event()` for state events.
278
+ ///
279
+ /// This function is used in the `RoomScreen` widget's `draw_walk()` method when the timeline is being rendered as a search result.
273
280
pub fn search_result_draw_walk ( room_screen : & mut RoomScreen , cx : & mut Cx2d , scope : & mut Scope , walk : Walk ) -> DrawStep {
274
281
let room_screen_widget_uid = room_screen. widget_uid ( ) ;
275
282
let Some ( room_id) = & room_screen. room_id else { return DrawStep :: done ( ) ; } ;
@@ -447,6 +454,13 @@ pub fn search_result_draw_walk(room_screen: &mut RoomScreen, cx: &mut Cx2d, scop
447
454
DrawStep :: done ( )
448
455
}
449
456
457
+ /// Copies the HTML content of a message to the clipboard if the message is a text message, notice, emote, image, file, audio, video, or verification request, and if it is formatted as HTML.
458
+ ///
459
+ /// If the message is an edit of another message, the function will copy the content of the original message instead of the edited message.
460
+ ///
461
+ /// Returns `true` if the content was copied successfully, and `false` otherwise.
462
+ ///
463
+ /// The function takes as input the current Makepad context, a reference to a `MessageDetails` struct, a reference to a `TimelineUiState` struct, and a mutable reference to a boolean to set to `true` if the content was copied successfully.
450
464
pub fn search_result_copy_html_to_clipboard ( cx : & mut Cx , details : & MessageDetails , tl : & TimelineUiState , success : & mut bool ) {
451
465
let ( Some ( SearchTimelineItem :: Event ( event) ) | Some ( SearchTimelineItem :: ContextEvent ( event) ) ) = tl. search_result_state . items
452
466
. get ( details. item_id ) else { return } ;
@@ -478,6 +492,11 @@ pub fn search_result_copy_html_to_clipboard(cx: &mut Cx, details: &MessageDetail
478
492
}
479
493
}
480
494
}
495
+ /// Copies the text of the message at the given item id to the clipboard.
496
+ ///
497
+ /// The item id must point to a message in the timeline, otherwise this function does nothing.
498
+ ///
499
+ /// If the message is a reply-to message, the body of the replied-to message is copied instead.
481
500
pub fn search_result_copy_to_clipboard ( cx : & mut Cx , details : & MessageDetails , tl : & TimelineUiState ) {
482
501
let ( Some ( SearchTimelineItem :: Event ( event) ) | Some ( SearchTimelineItem :: ContextEvent ( event) ) ) = tl. search_result_state . items
483
502
. get ( details. item_id ) else { return } ;
@@ -495,6 +514,12 @@ pub fn search_result_copy_to_clipboard(cx: &mut Cx, details: &MessageDetails, tl
495
514
}
496
515
}
497
516
}
517
+ /// React to a message in the timeline.
518
+ ///
519
+ /// The item id must point to a message in the timeline, otherwise this function does nothing.
520
+ ///
521
+ /// If the message is a reply-to message, the reaction is sent to the replied-to message instead.
522
+ ///
498
523
pub fn search_result_react ( _cx : & mut Cx , details : & MessageDetails , tl : & TimelineUiState , reaction : String , success : & mut bool ) {
499
524
let ( Some ( SearchTimelineItem :: Event ( event) ) | Some ( SearchTimelineItem :: ContextEvent ( event) ) ) = tl. search_result_state . items
500
525
. get ( details. item_id ) else { return } ;
@@ -518,6 +543,13 @@ pub fn search_result_react(_cx: &mut Cx, details: &MessageDetails, tl: &Timeline
518
543
* success = true ;
519
544
}
520
545
546
+ /// Reply to a message in the timeline.
547
+ ///
548
+ /// The item id must point to a message in the timeline, otherwise this function does nothing.
549
+ ///
550
+ /// If the message is a reply-to message, the reply is sent to the replied-to message instead.
551
+ ///
552
+ /// This function also hides the search results view and clears the search filter.
521
553
pub fn search_result_reply ( cx : & mut Cx , room_screen : & RoomScreen , details : & MessageDetails , tl : & TimelineUiState , success : & mut bool ) {
522
554
let ( Some ( SearchTimelineItem :: Event ( event) ) | Some ( SearchTimelineItem :: ContextEvent ( event) ) ) = tl. search_result_state . items
523
555
. get ( details. item_id ) else { return } ;
@@ -540,6 +572,11 @@ pub fn search_result_reply(cx: &mut Cx, room_screen: &RoomScreen, details: &Mess
540
572
) ;
541
573
* success = true ;
542
574
}
575
+ /// Finds the index of the timeline item in the main timeline items list
576
+ /// that the given search result item is related to (i.e. is a reply to).
577
+ ///
578
+ /// Returns `None` if the search result item is not a reply to a message
579
+ /// in the main timeline items list.
543
580
pub fn search_result_jump_to_related ( _cx : & mut Cx , details : & MessageDetails , tl : & TimelineUiState ) -> Option < usize > {
544
581
let ( Some ( SearchTimelineItem :: Event ( event) ) | Some ( SearchTimelineItem :: ContextEvent ( event) ) ) = tl. search_result_state . items
545
582
. get ( details. item_id ) else { return None } ;
@@ -551,6 +588,83 @@ pub fn search_result_jump_to_related(_cx: &mut Cx, details: &MessageDetails, tl:
551
588
}
552
589
None
553
590
}
591
+
592
+ /// Handles any search-related actions received by this RoomScreen.
593
+ ///
594
+ /// See `RoomFilterAction` for the possible actions.
595
+ pub fn handle_search_input (
596
+ room_screen : & mut RoomScreen ,
597
+ cx : & mut Cx ,
598
+ action : & Action ,
599
+ scope : & mut Scope ,
600
+ ) {
601
+ let widget_action = action. as_widget_action ( ) ;
602
+ match widget_action. cast ( ) {
603
+ RoomFilterAction :: Changed ( search_term) => {
604
+
605
+ if let Some ( selected_room) = {
606
+ let app_state = scope. data . get :: < AppState > ( ) . unwrap ( ) ;
607
+ app_state. selected_room . clone ( )
608
+ } {
609
+ if Some ( selected_room. room_id ( ) ) == room_screen. room_id . as_ref ( ) {
610
+ if search_term. is_empty ( ) {
611
+ room_screen. search_result ( id ! ( search_result_plane) ) . reset ( cx) ;
612
+ room_screen. view ( id ! ( timeline) ) . set_visible ( cx, true ) ;
613
+ room_screen. view ( id ! ( search_timeline) ) . set_visible ( cx, false ) ;
614
+ room_screen. search_result ( id ! ( search_result_plane) ) . set_visible ( cx, false ) ;
615
+ return ;
616
+ }
617
+ room_screen. search_debounce_timer = cx. start_timeout ( 1.0 ) ;
618
+ if let Some ( ref mut tl_state) = room_screen. tl_state {
619
+ tl_state. search_result_state . items . clear ( ) ;
620
+ tl_state. search_result_state . highlighted_strings . clear ( ) ;
621
+ }
622
+ let mut criteria = room_screen. search_result ( id ! ( search_result_plane) ) . get_search_criteria ( ) ;
623
+ criteria. search_term = search_term. clone ( ) ;
624
+ room_screen. search_result ( id ! ( search_result_plane) ) . set_search_criteria ( cx, criteria) ;
625
+ room_screen. view ( id ! ( timeline) ) . set_visible ( cx, false ) ;
626
+ }
627
+ }
628
+ }
629
+ RoomFilterAction :: Click ( search_term) => {
630
+ if let Some ( selected_room) = {
631
+ let app_state = scope. data . get :: < AppState > ( ) . unwrap ( ) ;
632
+ app_state. selected_room . clone ( )
633
+ } {
634
+ if Some ( selected_room. room_id ( ) ) == room_screen. room_id . as_ref ( ) {
635
+ let mut criteria = room_screen. search_result ( id ! ( search_result_plane) ) . get_search_criteria ( ) ;
636
+ if search_term == criteria. search_term && !search_term. is_empty ( ) {
637
+ return ;
638
+ }
639
+ criteria. search_term = search_term. clone ( ) ;
640
+ room_screen. search_result ( id ! ( search_result_plane) ) . set_search_criteria ( cx, criteria) ;
641
+ room_screen. view ( id ! ( timeline) ) . set_visible ( cx, false ) ;
642
+ }
643
+ }
644
+ }
645
+ RoomFilterAction :: Clear => {
646
+ if let Some ( selected_room) = {
647
+ let app_state = scope. data . get :: < AppState > ( ) . unwrap ( ) ;
648
+ app_state. selected_room . clone ( )
649
+ } {
650
+ if Some ( selected_room. room_id ( ) ) == room_screen. room_id . as_ref ( ) {
651
+ cx. stop_timer ( room_screen. search_debounce_timer ) ;
652
+ let Some ( tl) = room_screen. tl_state . as_mut ( ) else { return } ;
653
+ tl. search_result_state . items . clear ( ) ;
654
+ tl. search_result_state . highlighted_strings . clear ( ) ;
655
+ room_screen. view ( id ! ( search_timeline) ) . set_visible ( cx, false ) ;
656
+ room_screen. view ( id ! ( timeline) ) . set_visible ( cx, true ) ;
657
+ room_screen. search_result ( id ! ( search_result_plane) ) . reset ( cx) ;
658
+ room_screen. search_result ( id ! ( search_result_plane) ) . set_visible ( cx, false ) ;
659
+ }
660
+ }
661
+ }
662
+ _ => {
663
+
664
+ }
665
+ }
666
+ }
667
+ /// Search result as timeline item
554
668
#[ derive( Clone , Debug ) ]
555
669
pub enum SearchTimelineItem {
556
670
/// The event that matches the search criteria.
@@ -559,7 +673,7 @@ pub enum SearchTimelineItem {
559
673
ContextEvent ( AnyTimelineEvent ) ,
560
674
/// A date divider used to separate each search results.
561
675
DateDivider ( MilliSecondsSinceUnixEpoch ) ,
562
- /// The room name for other's room messages.
676
+ /// The room id used for displaying room header for all searched messages in a screen .
563
677
RoomHeader ( OwnedRoomId ) ,
564
678
}
565
679
0 commit comments