Skip to content

Commit 00b9d3a

Browse files
committed
add doc, fix search_result issues.
1 parent 36fbbbc commit 00b9d3a

File tree

2 files changed

+122
-85
lines changed

2 files changed

+122
-85
lines changed

src/home/room_screen.rs

Lines changed: 6 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ use matrix_sdk_ui::timeline::{
2222
use ruma::{OwnedUserId, UserId};
2323

2424
use crate::{
25-
app::AppState, avatar_cache, event_preview::{plaintext_body_of_timeline_item, text_preview_of_member_profile_change, text_preview_of_other_state, text_preview_of_redacted_message, text_preview_of_room_membership_change, text_preview_of_timeline_item}, home::loading_pane::{LoadingPaneState, LoadingPaneWidgetExt}, location::init_location_subscriber, media_cache::{MediaCache, MediaCacheEntry}, profile::{
25+
avatar_cache, event_preview::{plaintext_body_of_timeline_item, text_preview_of_member_profile_change, text_preview_of_other_state, text_preview_of_redacted_message, text_preview_of_room_membership_change, text_preview_of_timeline_item}, home::loading_pane::{LoadingPaneState, LoadingPaneWidgetExt}, location::init_location_subscriber, media_cache::{MediaCache, MediaCacheEntry}, profile::{
2626
user_profile::{AvatarState, ShowUserProfileAction, UserProfile, UserProfileAndRoomId, UserProfilePaneInfo, UserProfileSlidingPaneRef, UserProfileSlidingPaneWidgetExt},
2727
user_profile_cache,
2828
}, shared::{
29-
avatar::AvatarWidgetRefExt, callout_tooltip::TooltipAction, html_or_plaintext::{HtmlOrPlaintextRef, HtmlOrPlaintextWidgetRefExt, RobrixHtmlLinkAction}, jump_to_bottom_button::{JumpToBottomButtonWidgetExt, UnreadMessageCount}, popup_list::enqueue_popup_notification, room_filter_input_bar::RoomFilterAction, styles::COLOR_DANGER_RED, text_or_image::{TextOrImageRef, TextOrImageWidgetRefExt}, typing_animation::TypingAnimationWidgetExt
29+
avatar::AvatarWidgetRefExt, callout_tooltip::TooltipAction, html_or_plaintext::{HtmlOrPlaintextRef, HtmlOrPlaintextWidgetRefExt, RobrixHtmlLinkAction}, jump_to_bottom_button::{JumpToBottomButtonWidgetExt, UnreadMessageCount}, popup_list::enqueue_popup_notification, styles::COLOR_DANGER_RED, text_or_image::{TextOrImageRef, TextOrImageWidgetRefExt}, typing_animation::TypingAnimationWidgetExt
3030
}, sliding_sync::{get_client, submit_async_request, take_timeline_endpoints, BackwardsPaginateUntilEventRequest, MatrixRequest, PaginationDirection, TimelineRequestSender, UserPowerLevels}, utils::{self, room_name_or_id, unix_time_millis_to_datetime, ImageFormat, MEDIA_THUMBNAIL_FORMAT}
3131
};
3232
use crate::home::event_reaction_list::ReactionListWidgetRefExt;
@@ -37,7 +37,7 @@ use crate::shared::mentionable_text_input::MentionableTextInputWidgetRefExt;
3737
use rangemap::RangeSet;
3838

3939
use super::{editing_pane::{EditingPaneAction, EditingPaneWidgetExt}, event_reaction_list::ReactionData, loading_pane::LoadingPaneRef, location_preview::LocationPreviewWidgetExt, new_message_context_menu::{MessageAbilities, MessageDetails}, room_read_receipt::{self, populate_read_receipts, MAX_VISIBLE_AVATARS_IN_READ_RECEIPT}, rooms_list::RoomsListWidgetExt};
40-
use search_result::SearchResultWidgetExt;
40+
use search_result::{handle_search_input, SearchResultWidgetExt};
4141
pub use search_result::SearchTimelineItem;
4242
const GEO_URI_SCHEME: &str = "geo:";
4343

@@ -1010,7 +1010,7 @@ impl Widget for RoomScreen {
10101010
let message_input = self.room_input_bar(id!(input_bar)).text_input(id!(text_input));
10111011

10121012
for action in actions {
1013-
self.handle_search(cx, action, scope);
1013+
handle_search_input(self, cx, action, scope);
10141014
// Handle the highlight animation.
10151015
let Some(tl) = self.tl_state.as_mut() else { return };
10161016
if let MessageHighlightAnimationState::Pending { item_id } = tl.message_highlight_animation_state {
@@ -2600,83 +2600,6 @@ impl RoomScreen {
26002600
}
26012601
tl.last_scrolled_index = first_index;
26022602
}
2603-
2604-
2605-
/// Handles any search-related actions received by this RoomScreen.
2606-
///
2607-
/// See `SearchBarAction` for the possible actions.
2608-
fn handle_search(
2609-
&mut self,
2610-
cx: &mut Cx,
2611-
action: &Action,
2612-
scope: &mut Scope,
2613-
) {
2614-
let widget_action = action.as_widget_action();
2615-
match widget_action.cast() {
2616-
RoomFilterAction::Changed(search_term) => {
2617-
2618-
if let Some(selected_room) = {
2619-
let app_state = scope.data.get::<AppState>().unwrap();
2620-
app_state.selected_room.clone()
2621-
} {
2622-
if Some(selected_room.room_id()) == self.room_id.as_ref() {
2623-
if search_term.is_empty() {
2624-
self.search_result(id!(search_result_plane)).reset(cx);
2625-
self.view(id!(timeline)).set_visible(cx, true);
2626-
self.view(id!(search_timeline)).set_visible(cx, false);
2627-
self.search_result(id!(search_result_plane)).set_visible(cx, false);
2628-
return;
2629-
}
2630-
self.search_debounce_timer = cx.start_timeout(1.0);
2631-
if let Some(ref mut tl_state) = self.tl_state {
2632-
tl_state.search_result_state.items.clear();
2633-
tl_state.search_result_state.highlighted_strings.clear();
2634-
}
2635-
let mut criteria = self.search_result(id!(search_result_plane)).get_search_criteria();
2636-
criteria.search_term = search_term.clone();
2637-
self.search_result(id!(search_result_plane)).set_search_criteria(cx, criteria);
2638-
self.view(id!(timeline)).set_visible(cx, false);
2639-
}
2640-
}
2641-
}
2642-
RoomFilterAction::Click(search_term) => {
2643-
if let Some(selected_room) = {
2644-
let app_state = scope.data.get::<AppState>().unwrap();
2645-
app_state.selected_room.clone()
2646-
} {
2647-
if Some(selected_room.room_id()) == self.room_id.as_ref() {
2648-
let mut criteria = self.search_result(id!(search_result_plane)).get_search_criteria();
2649-
if search_term == criteria.search_term && !search_term.is_empty() {
2650-
return;
2651-
}
2652-
criteria.search_term = search_term.clone();
2653-
self.search_result(id!(search_result_plane)).set_search_criteria(cx, criteria);
2654-
self.view(id!(timeline)).set_visible(cx, false);
2655-
}
2656-
}
2657-
}
2658-
RoomFilterAction::Clear => {
2659-
if let Some(selected_room) = {
2660-
let app_state = scope.data.get::<AppState>().unwrap();
2661-
app_state.selected_room.clone()
2662-
} {
2663-
if Some(selected_room.room_id()) == self.room_id.as_ref() {
2664-
cx.stop_timer(self.search_debounce_timer);
2665-
let Some(tl) = self.tl_state.as_mut() else { return };
2666-
tl.search_result_state.items.clear();
2667-
tl.search_result_state.highlighted_strings.clear();
2668-
self.view(id!(search_timeline)).set_visible(cx, false);
2669-
self.view(id!(timeline)).set_visible(cx, true);
2670-
self.search_result(id!(search_result_plane)).reset(cx);
2671-
self.search_result(id!(search_result_plane)).set_visible(cx, false);
2672-
}
2673-
}
2674-
}
2675-
_ => {
2676-
2677-
}
2678-
}
2679-
}
26802603

26812604
}
26822605

@@ -2916,14 +2839,14 @@ pub struct TimelineUiState {
29162839
/// State used to display search results.
29172840
search_result_state: SearchResultState,
29182841
}
2842+
/// States that are necessary to display search results.
29192843
#[derive(Default)]
29202844
pub struct SearchResultState {
29212845
/// The list of events in the search results.
29222846
items: Vector<SearchTimelineItem>,
29232847
/// The list of strings that should be highlighted in the search results.
29242848
highlighted_strings: Vec<String>,
2925-
/// Upon a background update, only item indices greater than or equal to the
2926-
/// `index_of_first_change` are removed from this set.
2849+
/// See [`TimelineUiState.content_drawn_since_last_update`].
29272850
content_drawn_since_last_update: RangeSet<usize>,
29282851
/// Same as `content_drawn_since_last_update`, but for the event **profiles** (avatar, username).
29292852
profile_drawn_since_last_update: RangeSet<usize>,

src/home/room_search_result.rs

Lines changed: 116 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use makepad_widgets::*;
55
use matrix_sdk_ui::timeline::{AnyOtherFullStateEventContent, InReplyToDetails, Profile, ReactionsByKeyBySender, TimelineDetails, TimelineEventItemId};
66
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};
77

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};
99

1010
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};
1111

@@ -270,6 +270,13 @@ impl SearchResultRef {
270270
}
271271
}
272272

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.
273280
pub fn search_result_draw_walk(room_screen: &mut RoomScreen, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
274281
let room_screen_widget_uid = room_screen.widget_uid();
275282
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
447454
DrawStep::done()
448455
}
449456

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.
450464
pub fn search_result_copy_html_to_clipboard(cx: &mut Cx, details: &MessageDetails, tl: &TimelineUiState, success: &mut bool) {
451465
let (Some(SearchTimelineItem::Event(event)) | Some(SearchTimelineItem::ContextEvent(event))) = tl.search_result_state.items
452466
.get(details.item_id) else { return };
@@ -478,6 +492,11 @@ pub fn search_result_copy_html_to_clipboard(cx: &mut Cx, details: &MessageDetail
478492
}
479493
}
480494
}
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.
481500
pub fn search_result_copy_to_clipboard(cx: &mut Cx, details: &MessageDetails, tl: &TimelineUiState) {
482501
let (Some(SearchTimelineItem::Event(event)) | Some(SearchTimelineItem::ContextEvent(event))) = tl.search_result_state.items
483502
.get(details.item_id) else { return };
@@ -495,6 +514,12 @@ pub fn search_result_copy_to_clipboard(cx: &mut Cx, details: &MessageDetails, tl
495514
}
496515
}
497516
}
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+
///
498523
pub fn search_result_react(_cx: &mut Cx, details: &MessageDetails, tl: &TimelineUiState, reaction: String, success: &mut bool) {
499524
let (Some(SearchTimelineItem::Event(event)) | Some(SearchTimelineItem::ContextEvent(event))) = tl.search_result_state.items
500525
.get(details.item_id) else { return };
@@ -518,6 +543,13 @@ pub fn search_result_react(_cx: &mut Cx, details: &MessageDetails, tl: &Timeline
518543
*success = true;
519544
}
520545

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.
521553
pub fn search_result_reply(cx: &mut Cx, room_screen: &RoomScreen, details: &MessageDetails, tl: &TimelineUiState, success: &mut bool) {
522554
let (Some(SearchTimelineItem::Event(event)) | Some(SearchTimelineItem::ContextEvent(event))) = tl.search_result_state.items
523555
.get(details.item_id) else { return };
@@ -540,6 +572,11 @@ pub fn search_result_reply(cx: &mut Cx, room_screen: &RoomScreen, details: &Mess
540572
);
541573
*success = true;
542574
}
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.
543580
pub fn search_result_jump_to_related(_cx: &mut Cx, details: &MessageDetails, tl: &TimelineUiState)-> Option<usize> {
544581
let (Some(SearchTimelineItem::Event(event)) | Some(SearchTimelineItem::ContextEvent(event))) = tl.search_result_state.items
545582
.get(details.item_id) else { return None};
@@ -551,6 +588,83 @@ pub fn search_result_jump_to_related(_cx: &mut Cx, details: &MessageDetails, tl:
551588
}
552589
None
553590
}
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
554668
#[derive(Clone, Debug)]
555669
pub enum SearchTimelineItem {
556670
/// The event that matches the search criteria.
@@ -559,7 +673,7 @@ pub enum SearchTimelineItem {
559673
ContextEvent(AnyTimelineEvent),
560674
/// A date divider used to separate each search results.
561675
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.
563677
RoomHeader(OwnedRoomId),
564678
}
565679

0 commit comments

Comments
 (0)