diff --git a/src/home/room_screen.rs b/src/home/room_screen.rs index 6fc470a7..12ae7b1c 100644 --- a/src/home/room_screen.rs +++ b/src/home/room_screen.rs @@ -12,9 +12,8 @@ use matrix_sdk::{ message::{ AudioMessageEventContent, CustomEventContent, EmoteMessageEventContent, FileMessageEventContent, FormattedBody, ImageMessageEventContent, KeyVerificationRequestEventContent, LocationMessageEventContent, MessageFormat, MessageType, NoticeMessageEventContent, RoomMessageEventContent, ServerNoticeMessageEventContent, TextMessageEventContent, VideoMessageEventContent }, ImageInfo, MediaSource - }, sticker::StickerEventContent}, matrix_uri::MatrixId, uint, EventId, MatrixToUri, MatrixUri, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomId, UserId - }, - OwnedServerName, + }, sticker::StickerEventContent}, matrix_uri::MatrixId, uint, EventId, MatrixToUri, MatrixUri, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedMxcUri, OwnedRoomId, UserId + }, OwnedServerName }; use matrix_sdk_ui::timeline::{ self, EventTimelineItem, InReplyToDetails, MemberProfileChange, Profile, ReactionsByKeyBySender, RepliedToInfo, RoomMembershipChange, TimelineDetails, TimelineItem, TimelineItemContent, TimelineItemKind, VirtualTimelineItem @@ -27,7 +26,7 @@ use crate::{ user_profile_cache, }, shared::{ avatar::{AvatarRef, AvatarWidgetRefExt}, html_or_plaintext::{HtmlOrPlaintextRef, HtmlOrPlaintextWidgetRefExt}, jump_to_bottom_button::{JumpToBottomButtonWidgetExt, UnreadMessageCount}, text_or_image::{TextOrImageRef, TextOrImageWidgetRefExt}, typing_animation::TypingAnimationWidgetExt - }, sliding_sync::{self, get_client, submit_async_request, take_timeline_endpoints, BackwardsPaginateUntilEventRequest, MatrixRequest, PaginationDirection, TimelineRequestSender}, utils::{self, unix_time_millis_to_datetime, ImageFormat, MediaFormatConst}, + }, sliding_sync::{self, get_client, submit_async_request, take_timeline_endpoints, BackwardsPaginateUntilEventRequest, MatrixRequest, PaginationDirection, TimelineRequestSender}, utils::{self, unix_time_millis_to_datetime, ImageFormat, MediaFormatConst}, home::message_context_menu::MessageActionBarWidgetRefExt, }; use rangemap::RangeSet; @@ -56,6 +55,7 @@ live_design! { use crate::shared::typing_animation::TypingAnimation; use crate::shared::icon_button::*; use crate::shared::jump_to_bottom_button::*; + use crate::shared::image_viewer::ImageViewer; use crate::home::loading_modal::*; use crate::home::message_context_menu::*; @@ -688,6 +688,9 @@ live_design! { draw_bg: { color: (COLOR_PRIMARY_DARKER) } + image_viewer = { + + } keyboard_view = { width: Fill, height: Fill, @@ -1156,7 +1159,7 @@ impl Widget for RoomScreen { content: { margin: { left: (coords.x), top: (coords.y) } } }, ); - + if let Some(message_widget_uid) = action.as_widget_action().map(|a| a.widget_uid) { message_action_bar_popup.open(cx); message_action_bar.initialize_with_data(cx, widget_uid, message_widget_uid, item_id); @@ -1546,7 +1549,7 @@ impl RoomScreen { submit_async_request(MatrixRequest::GetNumberUnreadMessages{ room_id: room_id.clone() }); } } - + if clear_cache { tl.content_drawn_since_last_update.clear(); tl.profile_drawn_since_last_update.clear(); @@ -2147,7 +2150,7 @@ impl RoomScreen { event_id: last_event_id.to_owned(), }); } - + } } } @@ -3075,52 +3078,74 @@ fn populate_image_message_content( } } - match image_info_source.map(|(_, source)| source) { - Some(MediaSource::Plain(mxc_uri)) => { - // now that we've obtained the image URI and its metadata, try to fetch the image. - match media_cache.try_get_media_or_fetch(mxc_uri.clone(), None) { - MediaCacheEntry::Loaded(data) => { - let show_image_result = text_or_image_ref.show_image(|img| { - utils::load_png_or_jpg(&img, cx, &data) - .map(|()| img.size_in_pixels(cx).unwrap()) - }); - if let Err(e) = show_image_result { - let err_str = format!("{body}\n\nFailed to display image: {e:?}"); - error!("{err_str}"); - text_or_image_ref.show_text(&err_str); - } - - // We're done drawing the image message content, so mark it as fully drawn. - true - } - MediaCacheEntry::Requested => { - text_or_image_ref.show_text(format!("{body}\n\nFetching image from {:?}", mxc_uri)); - // Do not consider this image as being fully drawn, as we're still fetching it. - false - } - MediaCacheEntry::Failed => { - text_or_image_ref - .show_text(format!("{body}\n\nFailed to fetch image from {:?}", mxc_uri)); - // For now, we consider this as being "complete". In the future, we could support - // retrying to fetch the image on a user click/tap. - true + let mut fully_drawn = true; + let mut fetch_and_show_image = |mxc_uri: OwnedMxcUri| + match media_cache.try_get_media_or_fetch(mxc_uri.clone(), None) { + MediaCacheEntry::Loaded(data) => { + let show_image_result = text_or_image_ref.show_image(|img| { + utils::load_png_or_jpg(&img, cx, &data) + .map(|()| img.size_in_pixels(cx).unwrap_or_default()) + }); + if let Err(e) = show_image_result { + let err_str = format!("{body}\n\nFailed to display image: {e:?}"); + error!("{err_str}"); + text_or_image_ref.show_text(&err_str); } + + // We're done drawing thumbnail of the image message content, so mark it as fully drawn. } - } - Some(MediaSource::Encrypted(encrypted)) => { + MediaCacheEntry::Requested => { + text_or_image_ref.show_text(format!("{body}\n\nFetching image from {:?}", mxc_uri)); + // Do not consider this thumbnail as being fully drawn, as we're still fetching it. + fully_drawn = false; + } + MediaCacheEntry::Failed => { + text_or_image_ref + .show_text(format!("{body}\n\nFailed to fetch image from {:?}", mxc_uri)); + // For now, we consider this as being "complete". In the future, we could support + // retrying to fetch thumbnail of the image on a user click/tap. + } + }; + + match image_info_source { + Some((None, MediaSource::Plain(mxc_uri))) => { + // We fetch the origin of the media if its thumbnail is `None`. + fetch_and_show_image(mxc_uri.clone()); + }, + + Some((None, MediaSource::Encrypted(encrypted))) => { text_or_image_ref.show_text(format!( "{body}\n\n[TODO] fetch encrypted image at {:?}", encrypted.url )); // We consider this as "fully drawn" since we don't yet support encryption. - true - } - None => { - text_or_image_ref.show_text("{body}\n\nImage message had no source URL."); - true - } + }, + Some((Some(image_info), MediaSource::Plain(mxc_uri))) => { + match image_info.thumbnail_source { + Some(MediaSource::Plain(thumbnail_mxc_uri)) => { + // Now that we've obtained thumbnail of the image URI and its metadata. + // Let's fetch it. + fetch_and_show_image(thumbnail_mxc_uri.clone()); + }, + Some(MediaSource::Encrypted(encrypted)) => { + text_or_image_ref.show_text(format!( + "{body}\n\n[TODO] fetch encrypted image at {:?}", + encrypted.url + )); + // We consider this as "fully drawn" since we don't yet support encryption. + }, + None => { + // We fetch origin of the media again if `image_info.thumbnail_source` is None. + fetch_and_show_image(mxc_uri.clone()); + } + } + }, + + _ => { } } + + fully_drawn } diff --git a/src/shared/clickable_view.rs b/src/shared/clickable_view.rs index 41a84fb2..fb5f056b 100644 --- a/src/shared/clickable_view.rs +++ b/src/shared/clickable_view.rs @@ -47,4 +47,9 @@ impl ClickableViewRef { } false } + pub fn set_visible(&self, visible: bool) { + if let Some(mut inner) = self.borrow_mut() { + inner.visible = visible + } + } } diff --git a/src/shared/text_or_image.rs b/src/shared/text_or_image.rs index e3c99b7f..4518763d 100644 --- a/src/shared/text_or_image.rs +++ b/src/shared/text_or_image.rs @@ -9,8 +9,9 @@ live_design! { use link::theme::*; use link::shaders::*; use link::widgets::*; - + use crate::shared::styles::*; + use crate::shared::clickable_view::ClickableView; pub TextOrImage = {{TextOrImage}} { width: Fill, height: Fit, @@ -34,7 +35,7 @@ live_design! { } image_view = { visible: false, - cursor: NotAllowed, // we don't yet support clicking on the image + cursor: Hand, width: Fill, height: Fit, image = { width: Fill, height: Fit, @@ -44,7 +45,6 @@ live_design! { } } - /// A view that holds an image or text content, and can switch between the two. /// /// This is useful for displaying alternate text when an image is not (yet) available @@ -67,6 +67,7 @@ impl Widget for TextOrImage { self.view.draw_walk(cx, scope, walk) } } + impl TextOrImage { /// Sets the text content, which will be displayed on future draw operations. ///