Skip to content

Conversation

alanpoon
Copy link
Contributor

@alanpoon alanpoon commented Aug 6, 2025

Fixes #327
Screenshot 2025-08-06 at 5 46 55 PM

in replacement of #443

Waiting for this PR to be merged: makepad/makepad#788.

@alanpoon alanpoon self-assigned this Aug 6, 2025
@alanpoon alanpoon added the blocked-on-makepad Blocked on a Makepad bug or missing Makepad feature label Aug 11, 2025
@alanpoon
Copy link
Contributor Author

Intention of adding an image magnifier and the ability to pan image.

The image's image_scale, image_pan will always be set to default in draw_walk. Hence unable to dynamically scale and pan image.
makepad/makepad#785

@kevinaboos
Copy link
Member

Intention of adding an image magnifier and the ability to pan image.

The image's image_scale, image_pan will always be set to default in draw_walk. Hence unable to dynamically scale and pan image. makepad/makepad#785

@alanpoon I just checked with Rik, and this line should not be present in the Image widget. It's some errant code left over from dealing with animations in a strange way.

You can submit a PR to makepad that removes that line, and then continue with this issue here.

@kevinaboos kevinaboos added waiting-on-author This issue is waiting on the original author for a response and removed blocked-on-makepad Blocked on a Makepad bug or missing Makepad feature labels Oct 17, 2025
@alanpoon
Copy link
Contributor Author

Sure, the makepad PR is here: makepad/makepad#788

@alanpoon
Copy link
Contributor Author

Screenshot 2025-10-19 at 10 58 51 AM Screenshot 2025-10-19 at 10 58 57 AM Screenshot 2025-10-19 at 10 59 17 AM

@alanpoon alanpoon added waiting-on-review This issue is waiting to be reviewed and removed waiting-on-author This issue is waiting on the original author for a response labels Oct 19, 2025
Copy link
Member

@kevinaboos kevinaboos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Alan, nice work here.

My main comment is that we should decouple the ImageViewer widget from the RoomScreen. It doesn't need to know anything about the RoomScreen except for being able to access the RoomScreen's MediaCache instance (technically even that is not required, see Aarav's PR for an example of just passing the image data directly from the media fetch background task to the ImageViewer via an action).

Also, Aarav and I had a lot of discussions about using actions to communicate with the ImageViewer widget in PR#443. I think you can combine your approach (of storing images in the RoomScreen's MediaCache) with the action-based design from #443 (in which you emit an action including the image data/status instead of directly accessing the widget and calling a function on it). Actions are more idiomatic and also allow easy communication from any context, both in a background task and in a RoomScreen TimelineUpdate handler.

/// 1. Optionally initializes the modal with a new MXC URI
/// 2. Attempts to fetch or retrieve cached media
/// 3. Updates the UI based on the current media state: loading, loaded and failed.
fn populate_image_modal(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be moved into the image viewer widget module.

Comment on lines 1341 to 1344
if let LoadState::Loaded = populate_image_modal(cx, &mut self.image_viewer_timeout_timer, None, tl) {
let image_viewer_modal = get_global_image_viewer_modal(cx);
image_viewer_modal.set_image_loaded();
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is an unusual code flow/design. One would expect the ImageViewer to be completely independent from the timeline, as we'd like to use the same ImageViewer for any image across the app (including user Avatars, room Avatars, etc), not just images in a specific timeline.

Aarav and I already had this discussion in PR #443. In my opinion, the flow of actions there are much clearer and not dependent upon the timeline.

I don't mind storing the full-size image in the timeline's media cache, but the ImageViewer should also be able to watch for actions when media is fetched, rather than exclusively relying on the RoomScreen widget. What happens if the RoomScreen is closed or otherwise not receiving updates by the time that the full-size image is fetched?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, with an action-based design, we don't need the ImageViewer to be a global instance.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue with Aarav's action-based design is that clicking the image multiple times will cause memory spikes. As the action is sending large image bytes multiple times.

populate_image_modal(cx, &mut self.image_viewer_timeout_timer, Some(mxc_uri), tl);
}
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we have a better way of doing this now, you can handle this properly by moving it to L856, and in that case you don't have to check for the RoomId or even include the RoomId in the action variant.

/// Whether or not all rooms have been loaded (received from the homeserver).
#[rust] all_rooms_loaded: bool,
/// Timer for displaying `timeout` in the image viewer modal.
#[rust] image_viewer_timeout_timer: Timer
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you need a timeout timer, it would be best to put it in the Image viewer widget itself, not in an unrelated widget like the RoomScreen.

However, I don't think you actually need a separate timer here. The fetch-media network request will naturally return an error if it times out, so you should just rely on that and treat it just like any other fetch error. It doesn't make sense to have a special case for time outs.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that way, you can also display other errors (e.g., no connection, image deleted, invalid URI, etc)

// Here we set the global singleton for the PopupList widget,
// which is used to access PopupList Widget from anywhere in the app.
crate::shared::popup_list::set_global_popup_list(cx, &self.ui);
crate::shared::image_viewer_modal::set_global_image_viewer_modal(cx, self.ui.image_viewer_modal(id!(image_viewer_modal)));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove. This shouldn't be necessary.

pub ICON_LINK = dep("crate://self/resources/icons/link.svg")
pub ICON_PIN = dep("crate://self/resources/icons/pin.svg")
pub ICON_REPLY = dep("crate://self/resources/icons/reply.svg")
pub ICON_SEARCH = dep("crate://self/resources/icons/search.svg")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The icon used in the Image viewer should be a magnifying class with a +/- in it, not a search icon.

if let Some(room_props) = scope.props.get::<RoomScreenProps>() {
let room_id = room_props.room_id.clone();
// Send an Action containing the room_id and MXC URI to the room_screen
cx.widget_action(self.widget_uid(), &scope.path, TextOrImageAction::Clicked(room_id, mxc_uri.clone()));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't always guarantee that a TextOrImage widget is a child of a RoomScreen widget, so we shouldn't attempt to access RoomScreenProps via scope.

However, RoomId shouldn't be necessary at all here (see my other comments), so we can remove this entirely.

cx.set_cursor(MouseCursor::Hand);
}
Hit::FingerHoverOut(_) => {
cx.set_cursor(MouseCursor::Arrow);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you want Default here.

}
}

timeout_label_view = <View> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In keeping with my other comments, this should be a general error label, not limited to just time outs. The error from the network fetch operation should be shown to the user here (with a user-friendly message).

use link::tsp_link::TspVerificationModal;
use crate::shared::image_viewer_modal::ImageViewerModal;

APP_TAB_COLOR = #344054
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of this AppTab stuff is old. Looks like a merge conflict mistake.

@kevinaboos kevinaboos added waiting-on-author This issue is waiting on the original author for a response and removed waiting-on-review This issue is waiting to be reviewed labels Oct 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

waiting-on-author This issue is waiting on the original author for a response

Projects

None yet

Development

Successfully merging this pull request may close these issues.

When the user clicks a thumbnail image, show the full-size image in an image viewer widget

2 participants