diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2ea7bdb1c..180183ca2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -83,3 +83,11 @@ jobs: - uses: actions/checkout@v4 - name: Check for typos uses: crate-ci/typos@master + setup-deepseek-review: + runs-on: ubuntu-latest + name: Code Review + steps: + - name: DeepSeek Code Review + uses: hustcer/deepseek-review@v1 + with: + chat-token: ${{ secrets.CHAT_TOKEN }} diff --git a/Cargo.lock b/Cargo.lock index 564f7f0fc..706763b38 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3459,6 +3459,7 @@ dependencies = [ "robius-location", "robius-open", "robius-use-makepad", + "ruma", "serde", "serde_json", "tokio", diff --git a/Cargo.toml b/Cargo.toml index aab6616fd..6cebb7955 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,6 +54,7 @@ bytesize = "1.3.0" bitflags = "2.6.0" indexmap = "2.6.0" blurhash = { version = "0.2.3", default-features = false } +ruma = { git = "https://github.com/ruma/ruma", rev = "7755c7cbc580f8d8aea30d78cc1a6850b1a6fd39", default-features = false, features = ["api", "client-api-c"] } [package.metadata.docs.rs] all-features = true diff --git a/src/app.rs b/src/app.rs index 2f8949ee5..b48739a0e 100644 --- a/src/app.rs +++ b/src/app.rs @@ -4,7 +4,7 @@ use makepad_widgets::*; use matrix_sdk::ruma::OwnedRoomId; use crate::{ - home::{main_desktop_ui::RoomsPanelAction, new_message_context_menu::NewMessageContextMenuWidgetRefExt, room_screen::MessageAction, rooms_list::RoomsListAction}, login::login_screen::LoginAction, shared::{callout_tooltip::{CalloutTooltipOptions, CalloutTooltipWidgetRefExt, TooltipAction}, popup_list::PopupNotificationAction}, verification::VerificationAction, verification_modal::{VerificationModalAction, VerificationModalWidgetRefExt} + home::{main_desktop_ui::RoomsPanelAction, new_message_context_menu::NewMessageContextMenuWidgetRefExt, room_screen::MessageAction, rooms_list::RoomsListAction}, login::login_screen::LoginAction, shared::{callout_tooltip::{CalloutTooltipOptions, CalloutTooltipWidgetRefExt, TooltipAction}, popup_list::PopupNotificationAction, search_bar::SearchBarWidgetRefExt}, verification::VerificationAction, verification_modal::{VerificationModalAction, VerificationModalWidgetRefExt} }; live_design! { @@ -193,6 +193,7 @@ impl MatchEvent for App { self.update_login_visibility(cx); log!("App::handle_startup(): starting matrix sdk loop"); + self.app_state.search_widget = Some(self.ui.search_bar(id!(home_screen_search_bar)).widget_uid()); crate::sliding_sync::start_matrix_tokio().unwrap(); } @@ -304,7 +305,6 @@ impl MatchEvent for App { if let VerificationModalAction::Close = action.as_widget_action().cast() { self.ui.modal(id!(verification_modal)).close(cx); } - // // message source modal handling. // match action.as_widget_action().cast() { // MessageAction::MessageSourceModalOpen { room_id: _, event_id: _, original_json: _ } => { @@ -407,6 +407,10 @@ pub struct AppState { pub logged_in: bool, /// The current window geometry. pub window_geom: Option, + /// Main Desktop Ui's search widget uid. + /// Room screens will get this widget uid to differentiate search action + /// from search bar widget. + pub search_widget: Option, } #[derive(Default, Debug)] diff --git a/src/event_preview.rs b/src/event_preview.rs index ae857b717..1b59d9b7e 100644 --- a/src/event_preview.rs +++ b/src/event_preview.rs @@ -75,7 +75,7 @@ pub fn text_preview_of_timeline_item( text_preview_of_member_profile_change(profile_change, sender_username) } TimelineItemContent::OtherState(other_state) => { - text_preview_of_other_state(other_state) + text_preview_of_other_state(other_state.content(),other_state.state_key()) .unwrap_or_else(|| TextPreview::from(( String::from("initiated another state change"), BeforeText::UsernameWithoutColon, @@ -132,7 +132,7 @@ pub fn body_of_timeline_item( ).text } TimelineItemContent::OtherState(other_state) => { - text_preview_of_other_state(other_state) + text_preview_of_other_state(other_state.content(), other_state.state_key()) .unwrap_or_else(|| TextPreview::from(( String::from("initiated another state change."), BeforeText::UsernameWithoutColon, @@ -290,12 +290,12 @@ pub fn text_preview_of_redacted_message( TextPreview::from((text, BeforeText::Nothing)) } - /// Returns a text preview of the given other state event as an Html-formatted string. pub fn text_preview_of_other_state( - other_state: &timeline::OtherState, + other_state: &AnyOtherFullStateEventContent, + state_key: &str ) -> Option { - let text = match other_state.content() { + let text = match other_state { AnyOtherFullStateEventContent::RoomAliases(FullStateEventContent::Original { content, .. }) => { let mut s = String::from("set this room's aliases to "); let last_alias = content.aliases.len() - 1; @@ -370,10 +370,10 @@ pub fn text_preview_of_other_state( Some(format!("changed this room's topic to {:?}.", content.topic)) } AnyOtherFullStateEventContent::SpaceParent(_) => { - Some(format!("set this room's parent space to {}.", other_state.state_key())) + Some(format!("set this room's parent space to {}.", state_key)) } AnyOtherFullStateEventContent::SpaceChild(_) => { - Some(format!("added a new child to this space: {}.", other_state.state_key())) + Some(format!("added a new child to this space: {}.", state_key)) } _other => { // log!("*** Unhandled: {:?}.", _other); @@ -382,8 +382,6 @@ pub fn text_preview_of_other_state( }; text.map(|t| TextPreview::from((t, BeforeText::UsernameWithoutColon))) } - - /// Returns a text preview of the given member profile change as a plaintext string. pub fn text_preview_of_member_profile_change( change: &MemberProfileChange, diff --git a/src/home/home_screen.rs b/src/home/home_screen.rs index 483931049..09ce6957a 100644 --- a/src/home/home_screen.rs +++ b/src/home/home_screen.rs @@ -31,7 +31,7 @@ live_design! { { flow: Down width: Fill, height: Fill - {} + home_screen_search_bar = {} {} } } diff --git a/src/home/main_desktop_ui.rs b/src/home/main_desktop_ui.rs index ff195bf61..0898a39f8 100644 --- a/src/home/main_desktop_ui.rs +++ b/src/home/main_desktop_ui.rs @@ -169,7 +169,7 @@ impl MainDesktopUI { // create a new tab for the room let (tab_bar, _pos) = dock.find_tab_bar_of_tab(live_id!(home_tab)).unwrap(); - let kind = live_id!(room_screen); + let kind: LiveId = live_id!(room_screen); let result = dock.create_and_select_tab( cx, @@ -234,10 +234,10 @@ impl MainDesktopUI { self.most_recently_selected_room = None; } } - dock.close_tab(cx, tab_id); self.tab_to_close = None; self.open_rooms.remove(&tab_id); + dock.item(tab_id).as_room_screen().close_search(cx); } } diff --git a/src/home/mod.rs b/src/home/mod.rs index 8c1fdf799..771b6b9ba 100644 --- a/src/home/mod.rs +++ b/src/home/mod.rs @@ -9,6 +9,7 @@ pub mod main_mobile_ui; pub mod room_preview; pub mod room_screen; pub mod room_read_receipt; +pub mod room_search_result; pub mod rooms_list; pub mod rooms_sidebar; pub mod spaces_dock; @@ -24,6 +25,7 @@ pub fn live_design(cx: &mut Cx) { editing_pane::live_design(cx); new_message_context_menu::live_design(cx); room_screen::live_design(cx); + room_search_result::live_design(cx); room_read_receipt::live_design(cx); rooms_sidebar::live_design(cx); main_mobile_ui::live_design(cx); diff --git a/src/home/new_message_context_menu.rs b/src/home/new_message_context_menu.rs index c5afe4819..cb1749406 100644 --- a/src/home/new_message_context_menu.rs +++ b/src/home/new_message_context_menu.rs @@ -4,11 +4,12 @@ use bitflags::bitflags; use makepad_widgets::*; use matrix_sdk::ruma::OwnedEventId; -use matrix_sdk_ui::timeline::EventTimelineItem; +use matrix_sdk_ui::timeline::InReplyToDetails; +use ruma::events::room::message::MessageType; use crate::sliding_sync::UserPowerLevels; -use super::room_screen::{MessageAction, MessageOrSticker}; +use super::room_screen::{MessageViewFromEvent, MessageAction, MessageOrSticker}; const BUTTON_HEIGHT: f64 = 30.0; // KEEP IN SYNC WITH BUTTON_HEIGHT BELOW const MENU_WIDTH: f64 = 215.0; // KEEP IN SYNC WITH MENU_WIDTH BELOW @@ -283,10 +284,10 @@ bitflags! { } } impl MessageAbilities { - pub fn from_user_power_and_event( + pub fn from_user_power_and_event( user_power_levels: &UserPowerLevels, - event_tl_item: &EventTimelineItem, - _message: &MessageOrSticker, + event_tl_item: &T, + _message: &MessageOrSticker, has_html: bool, ) -> Self { let mut abilities = Self::empty(); @@ -305,7 +306,6 @@ impl MessageAbilities { abilities.set(Self::HasHtml, has_html); abilities } - } /// Details about the message that define its context menu content. @@ -657,3 +657,10 @@ impl NewMessageContextMenuRef { inner.show(cx, details) } } +/// A trait for event that can be used to show context menus. +pub trait ContextMenuFromEvent { + fn msgtype(&self) -> &MessageType; + fn body(&self) -> &str; + fn in_reply_to(&self) -> Option<&InReplyToDetails>; + fn is_searched_result(&self) -> bool; +} \ No newline at end of file diff --git a/src/home/room_read_receipt.rs b/src/home/room_read_receipt.rs index 12a8311b6..3d51f29a0 100644 --- a/src/home/room_read_receipt.rs +++ b/src/home/room_read_receipt.rs @@ -5,9 +5,10 @@ use crate::utils::human_readable_list; use indexmap::IndexMap; use makepad_widgets::*; use matrix_sdk::ruma::{events::receipt::Receipt, EventId, OwnedUserId, RoomId}; -use matrix_sdk_ui::timeline::EventTimelineItem; use std::cmp; +use super::room_screen::MessageViewFromEvent; + /// The maximum number of items to display in the read receipts AvatarRow /// and its accompanying tooltip. @@ -220,18 +221,20 @@ impl AvatarRowRef { /// room ID, and an EventTimelineItem, this will populate the avatar /// row of the item with the read receipts of the event. /// -pub fn populate_read_receipts( +pub fn populate_read_receipts( item: &WidgetRef, cx: &mut Cx, room_id: &RoomId, - event_tl_item: &EventTimelineItem, + event_tl_item: &T, ) { - item.avatar_row(id!(avatar_row)).set_avatar_row( - cx, - room_id, - event_tl_item.event_id(), - event_tl_item.read_receipts(), - ); + if let Some(read_receipts) = event_tl_item.read_receipts() { + item.avatar_row(id!(avatar_row)).set_avatar_row( + cx, + room_id, + event_tl_item.event_id(), + read_receipts, + ); + } } /// Populate the tooltip text for a read receipts avatar row. diff --git a/src/home/room_screen.rs b/src/home/room_screen.rs index 3e663532b..48ac4003f 100644 --- a/src/home/room_screen.rs +++ b/src/home/room_screen.rs @@ -1,10 +1,11 @@ //! A room screen is the UI page that displays a single Room's timeline of events/messages //! along with a message input bar at the bottom. -use std::{borrow::Cow, collections::BTreeMap, ops::{DerefMut, Range}, sync::{Arc, Mutex}, time::SystemTime}; +use std::{borrow::Cow, collections::{BTreeMap, HashMap}, ops::{DerefMut, Range}, sync::{Arc, Mutex}, time::SystemTime}; use bytesize::ByteSize; use imbl::Vector; +use indexmap::IndexMap; use makepad_widgets::{image_cache::ImageBuffer, *}; use matrix_sdk::{room::RoomMember, ruma::{ events::{receipt::Receipt, room::{ @@ -15,16 +16,17 @@ use matrix_sdk::{room::RoomMember, ruma::{ sticker::StickerEventContent, Mentions}, matrix_uri::MatrixId, uint, EventId, MatrixToUri, MatrixUri, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedMxcUri, OwnedRoomId }, OwnedServerName}; use matrix_sdk_ui::timeline::{ - self, EventTimelineItem, InReplyToDetails, MemberProfileChange, RepliedToInfo, RoomMembershipChange, TimelineDetails, TimelineEventItemId, TimelineItem, TimelineItemContent, TimelineItemKind, VirtualTimelineItem + self, EventTimelineItem, InReplyToDetails, MemberProfileChange, ReactionsByKeyBySender, RepliedToInfo, RoomMembershipChange, TimelineDetails, TimelineEventItemId, TimelineItem, TimelineItemContent, TimelineItemKind, VirtualTimelineItem }; use robius_location::Coordinates; +use ruma::{events::AnySyncTimelineEvent, serde::Raw, OwnedUserId, UserId}; use crate::{ - avatar_cache, event_preview::{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::{get_latest_location, init_location_subscriber, request_location_update, LocationAction, LocationRequest, LocationUpdate}, media_cache::{MediaCache, MediaCacheEntry}, profile::{ + app::AppState, avatar_cache, event_preview::{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}, room_search_result::{append_to_tl_state, SearchTimeline}}, location::{get_latest_location, init_location_subscriber, request_location_update, LocationAction, LocationRequest, LocationUpdate}, media_cache::{MediaCache, MediaCacheEntry}, profile::{ user_profile::{AvatarState, ShowUserProfileAction, UserProfile, UserProfileAndRoomId, UserProfilePaneInfo, UserProfileSlidingPaneRef, UserProfileSlidingPaneWidgetExt}, user_profile_cache, }, shared::{ - avatar::AvatarWidgetRefExt, callout_tooltip::TooltipAction, html_or_plaintext::{HtmlOrPlaintextRef, HtmlOrPlaintextWidgetRefExt, RobrixHtmlLinkAction}, jump_to_bottom_button::{JumpToBottomButtonWidgetExt, UnreadMessageCount}, popup_list::enqueue_popup_notification, text_or_image::{TextOrImageRef, TextOrImageWidgetRefExt}, typing_animation::TypingAnimationWidgetExt + avatar::AvatarWidgetRefExt, callout_tooltip::TooltipAction, html_or_plaintext::{HtmlOrPlaintextRef, HtmlOrPlaintextWidgetRefExt, RobrixHtmlLinkAction}, jump_to_bottom_button::{JumpToBottomButtonWidgetExt, UnreadMessageCount}, popup_list::enqueue_popup_notification, search_bar::SearchBarAction, text_or_image::{TextOrImageRef, TextOrImageWidgetRefExt}, typing_animation::TypingAnimationWidgetExt }, sliding_sync::{get_client, submit_async_request, take_timeline_endpoints, BackwardsPaginateUntilEventRequest, MatrixRequest, PaginationDirection, TimelineRequestSender, UserPowerLevels}, utils::{self, unix_time_millis_to_datetime, ImageFormat, MEDIA_THUMBNAIL_FORMAT} }; use crate::home::event_reaction_list::ReactionListWidgetRefExt; @@ -34,13 +36,13 @@ use crate::shared::mentionable_text_input::MentionableTextInputWidgetRefExt; use rangemap::RangeSet; -use super::{editing_pane::{EditingPaneAction, EditingPaneWidgetExt}, event_reaction_list::ReactionData, loading_pane::LoadingPaneRef, new_message_context_menu::{MessageAbilities, MessageDetails}, room_read_receipt::{self, populate_read_receipts, MAX_VISIBLE_AVATARS_IN_READ_RECEIPT}}; - +use super::{editing_pane::{EditingPaneAction, EditingPaneWidgetExt}, event_reaction_list::ReactionData, loading_pane::LoadingPaneRef, new_message_context_menu::{ContextMenuFromEvent, MessageAbilities, MessageDetails}, room_read_receipt::{self, populate_read_receipts, MAX_VISIBLE_AVATARS_IN_READ_RECEIPT}, room_search_result::{send_pagination_request_based_on_scroll_pos_for_search_result, CondensedSearchResult, SearchResultWidgetExt, SearchResultWidgetRefExt}, rooms_list::RoomsListWidgetExt}; const GEO_URI_SCHEME: &str = "geo:"; const MESSAGE_NOTICE_TEXT_COLOR: Vec3 = Vec3 { x: 0.5, y: 0.5, z: 0.5 }; const COLOR_DANGER_RED: Vec3 = Vec3 { x: 0.862, y: 0.0, z: 0.02 }; - +//LightGreen +const SEARCH_HIGHLIGHT: Vec3 = Vec3 { x: 0.89, y: 0.967, z: 0.929 }; live_design! { use link::theme::*; @@ -63,6 +65,8 @@ live_design! { use crate::home::event_reaction_list::*; use crate::home::editing_pane::*; use crate::room::room_input_bar::*; + use crate::home::room_search_result::*; + use crate::room::room_input_bar::*; IMG_DEFAULT_AVATAR = dep("crate://self/resources/img/default_avatar.png") @@ -312,70 +316,83 @@ live_design! { body = { width: Fill, - height: Fit - flow: Right, - padding: 10.0, + height: Fit, + flow: Overlay + body_inner = { - profile = { - align: {x: 0.5, y: 0.0} // centered horizontally, top aligned - width: 65.0, - height: Fit, - margin: {top: 4.5, right: 10} - flow: Down, - avatar = { - width: 50., - height: 50. - // draw_bg: { - // fn pixel(self) -> vec4 { - // let sdf = Sdf2d::viewport(self.pos * self.rect_size); - // let c = self.rect_size * 0.5; - // sdf.circle(c.x, c.y, c.x - 2.) - // sdf.fill_keep(self.get_color()); - // sdf.stroke((COLOR_PROFILE_CIRCLE), 1); - // return sdf.result - // } - // } - } - timestamp = { - padding: { top: 3.0 } - } - datestamp = { - padding: { top: 3.0 } - } - } - content = { width: Fill, height: Fit - flow: Down, - padding: 0.0 - { - flow: Right, - width: Fill, + flow: Right, + padding: 10.0, + + profile = { + align: {x: 0.5, y: 0.0} // centered horizontally, top aligned + width: 65.0, height: Fit, - username =