Skip to content

Commit f922cf2

Browse files
committed
image_upload improvement
1 parent 2afea24 commit f922cf2

File tree

8 files changed

+857
-158
lines changed

8 files changed

+857
-158
lines changed

Cargo.lock

Lines changed: 500 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ imbl = { version = "6.1.0", features = ["serde"] } # same as matrix-sdk
3737
futures-util = "0.3"
3838
htmlize = "1.0.5"
3939
imghdr = "0.7.0"
40+
image = { version = "0.25", default-features = false, features = ["jpeg", "png"] }
4041
linkify = "0.10.0"
4142
matrix-sdk-base = { git = "https://github.com/matrix-org/matrix-rust-sdk", branch = "main" }
4243
matrix-sdk = { git = "https://github.com/matrix-org/matrix-rust-sdk", branch = "main", default-features = false, features = [ "e2e-encryption", "automatic-room-key-forwarding", "markdown", "sqlite", "rustls-tls", "bundled-sqlite", "sso-login" ] }

src/app.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,7 @@ impl MatchEvent for App {
490490
self.ui.modal(ids!(file_upload_modal)).open(cx);
491491
continue;
492492
}
493-
Some(FilePreviewerAction::Hide) => {
493+
Some(FilePreviewerAction::Hide) | Some(FilePreviewerAction::Upload(_))=> {
494494
self.ui.modal(ids!(file_upload_modal)).close(cx);
495495
continue;
496496
}

src/home/room_screen.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1291,7 +1291,7 @@ impl RoomScreen {
12911291
if is_append && !portal_list.is_at_end() {
12921292
// Immediately show the unread badge with no count while we fetch the actual count in the background.
12931293
jump_to_bottom.show_unread_message_badge(cx, UnreadMessageCount::Unknown);
1294-
submit_async_request(MatrixRequest::GetNumberUnreadMessages{ room_id: tl.room_id.clone() });
1294+
submit_async_request(MatrixRequest::GetNumberUnreadMessages{ room_id: tl.room_id.clone() });
12951295
}
12961296

12971297
if prior_items_changed {

src/room/room_input_bar.rs

Lines changed: 236 additions & 18 deletions
Large diffs are not rendered by default.

src/shared/file_previewer.rs

Lines changed: 75 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,11 @@
1-
//! A file previewer widget that displays file metadata and previews.
1+
//! A file previewer modal widget that displays file metadata and previews.
22
//!
3-
//! This widget shows:
4-
//! - File metadata: filename and file size (always displayed)
5-
//! - For images: displays the image preview
6-
//! - For documents: displays only the filename
7-
//!
8-
//! ## Usage Example
9-
//!
10-
//! ```rust,ignore
11-
//! // Set the file metadata and file data
12-
//! file_previewer.set_content(cx, FileData(file_metadata, file_data)); // 500 KB file
3+
//! This widget handles FilePreviewerAction to show and hide the previewer modal.
4+
//! It also emits FilePreviewerAction::Upload action to upload the selected file.
135
//! ```
146
7+
use std::sync::Arc;
8+
159
use makepad_widgets::*;
1610
use makepad_widgets::image_cache::{ImageBuffer, ImageError};
1711
use mime_guess::{Mime, mime};
@@ -41,81 +35,74 @@ live_design! {
4135
use crate::shared::styles::*;
4236
use crate::shared::icon_button::RobrixIconButton;
4337

44-
FilePreviewerButton = <RobrixIconButton> {
45-
width: 44, height: 44
46-
align: {x: 0.5, y: 0.5},
47-
spacing: 0,
48-
padding: 0,
49-
draw_bg: {
50-
color: (COLOR_SECONDARY * 0.925)
51-
}
52-
draw_icon: {
53-
svg_file: (ICON_ZOOM_OUT),
54-
fn get_color(self) -> vec4 {
55-
return #x0;
56-
}
57-
}
58-
icon_walk: {width: 27, height: 27}
59-
}
60-
6138
pub FilePreviewer = {{FilePreviewer}} {
6239
width: Fit, height: Fit
6340
wrapper = <RoundedView> {
6441
width: Fit, height: Fit
6542
align: {x: 0.5}
6643
flow: Down
6744
padding: {top: 20, right: 20, bottom: 10, left: 20}
68-
6945
show_bg: true
7046
draw_bg: {
7147
color: (COLOR_PRIMARY)
7248
border_radius: 4
7349
}
7450

75-
header_view = <View> {
76-
width: 250
77-
height: Fit
78-
flow: Right
79-
align: {x: 1.0, y: 0.0}
80-
margin: {top: -15, right: -25, bottom: 10}
81-
82-
close_button = <FilePreviewerButton> {
83-
draw_icon: { svg_file: (ICON_CLOSE) }
84-
icon_walk: {width: 21, height: 21 }
85-
}
86-
}
8751
<View> {
8852
width: Fit, height: Fit
89-
flow: Right
90-
// Document view (visible only for non-image files)
91-
document_view = <View> {
92-
visible: false,
93-
width: Fit
94-
height: 40
95-
flow: Down
96-
align: {x: 0.5, y: 0.5}
97-
file_icon = <Icon> {
98-
draw_icon: {
99-
svg_file: (ICON_FILE),
100-
color: #999,
53+
flow: Down
54+
55+
<View> {
56+
width: Fit, height: Fit
57+
58+
header_label = <Label> {
59+
width: 200
60+
height: 50
61+
padding: 0
62+
//margin: {bottom: 15}
63+
draw_text: {
64+
text_style: <REGULAR_TEXT>{font_size: 15},
65+
color: (COLOR_TEXT),
66+
wrap: Word
10167
}
102-
icon_walk: { width: 20, height: 20 }
68+
text: "Upload Files"
10369
}
10470
}
105-
// File metadata section (always visible)
106-
metadata_view = <View> {
107-
width: 250
108-
height: Fit
109-
flow: Down
110-
spacing: 5
111-
112-
filename_text = <Label> {
113-
width: Fill
71+
<View> {
72+
width: Fit, height: Fit
73+
flow: Right
74+
75+
// Document view (visible only for non-image files)
76+
document_view = <View> {
77+
visible: false,
78+
width: Fit
79+
height: 40
80+
flow: Down
81+
align: {x: 0.5, y: 0.5}
82+
file_icon = <Icon> {
83+
draw_icon: {
84+
svg_file: (ICON_FILE),
85+
color: #999,
86+
}
87+
icon_walk: { width: 20, height: 20 }
88+
}
89+
}
90+
91+
// File metadata section (always visible)
92+
metadata_view = <View> {
93+
width: 250
11494
height: Fit
115-
draw_text: {
116-
text_style: <REGULAR_TEXT>{font_size: 14},
117-
color: #000,
118-
wrap: Word
95+
flow: Down
96+
spacing: 5
97+
98+
filename_text = <Label> {
99+
width: Fill
100+
height: Fit
101+
draw_text: {
102+
text_style: <REGULAR_TEXT>{font_size: 14},
103+
color: #000,
104+
wrap: Word
105+
}
119106
}
120107
}
121108
}
@@ -186,39 +173,40 @@ pub enum FilePreviewerAction {
186173
/// Display the FilePreviewer widget with the given file data.
187174
Show(FileData),
188175
/// Upload the file with the given data.
189-
Upload(FileData),
176+
Upload(FilePreviewerMetaData),
190177
/// Hide the FilePreviewer widget.
191178
Hide,
192179
/// No action.
193180
None,
194181
}
195182

196183
/// Type alias for file data message sent through the channel.
197-
pub type FileData = (FilePreviewerMetaData, Vec<u8>);
184+
pub type FileData = Arc<(FilePreviewerMetaData, Option<Vec<u8>>)>;
198185

199186
/// Type alias for the receiver that gets file data.
200-
pub type FileLoadReceiver = std::sync::mpsc::Receiver<FileData>;
187+
pub type FileLoadReceiver = std::sync::mpsc::Receiver<Option<FileData>>;
201188

202189
/// A widget that previews files by displaying metadata and content based on file type.
203190
#[derive(Live, Widget, LiveHook)]
204191
pub struct FilePreviewer {
205192
#[redraw] #[deref] view: View,
206193
#[rust] file_type: FileType,
207-
#[rust] file_data: Option<FileData>,
194+
#[rust] file_meta: Option<FilePreviewerMetaData>,
208195
}
196+
209197
impl FilePreviewer {
210198
/// Sets the file content to preview, including metadata and image/document display.
211199
/// For images, attempts to decode and display the preview. Falls back to document view on error.
212200
fn set_content(&mut self, cx: &mut Cx, file_load_message: FileData) {
213-
let (file_metadata, file_data) = file_load_message;
214-
self.set_metadata(cx, &file_metadata.filename, file_metadata.file_size as u64);
215-
216-
let close_button = self.view.button(ids!(wrapper.header_view.close_button));
217-
close_button.reset_hover(cx);
218-
close_button.set_enabled(cx, true);
219-
201+
let (file_metadata, file_data) = file_load_message.as_ref();
202+
self.file_meta = Some(file_metadata.clone());
203+
self.set_metadata(cx, &file_metadata.filename, file_metadata.file_size);
204+
220205
if file_metadata.mime.type_() == mime::IMAGE {
221206
// Attempt to decode the image data for preview
207+
let Some(file_data) = file_data else {
208+
return;
209+
};
222210
if let Ok(image_buffer) = load_image_from_bytes(&file_data) {
223211
// Get image dimensions to calculate aspect-ratio preserving size
224212
let (image_width, image_height) = (image_buffer.width, image_buffer.height);
@@ -271,50 +259,24 @@ impl Widget for FilePreviewer {
271259

272260
impl MatchEvent for FilePreviewer {
273261
fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions) {
274-
if self.view.button(ids!(wrapper.header_view.close_button)).clicked(actions) {
275-
cx.action(FilePreviewerAction::Hide);
276-
return;
277-
}
278-
279-
if self.view.button(ids!(close_modal_button)).clicked(actions) {
280-
cx.action(FilePreviewerAction::Hide);
281-
return;
282-
}
283262

284263
if self.view.button(ids!(wrapper.buttons_view.cancel_button)).clicked(actions) {
285264
cx.action(FilePreviewerAction::Hide);
286-
self.view.button(ids!(wrapper.buttons_view.cancel_button)).set_enabled(cx, false);
265+
self.file_meta = None;
287266
return;
288267
}
289268

290269
if self.view.button(ids!(wrapper.buttons_view.upload_button)).clicked(actions) {
291-
if let Some(file_data) = self.file_data.take() {
292-
cx.action(FilePreviewerAction::Upload(file_data));
293-
cx.action(FilePreviewerAction::Hide);
294-
self.view.button(ids!(wrapper.buttons_view.upload_button)).set_enabled(cx, false);
270+
if let Some(file_meta) = self.file_meta.take() {
271+
cx.action(FilePreviewerAction::Upload(file_meta));
295272
}
296273
return;
297274
}
298275

299276
for action in actions {
300277
if let Some(FilePreviewerAction::Show(file_data)) = action.downcast_ref() {
301-
// Reset all button states when showing the previewer
302-
let close_button = self.view.button(ids!(wrapper.header_view.close_button));
303-
close_button.reset_hover(cx);
304-
close_button.set_enabled(cx, true);
305-
306-
let cancel_button = self.view.button(ids!(wrapper.buttons_view.cancel_button));
307-
cancel_button.reset_hover(cx);
308-
cancel_button.set_enabled(cx, true);
309-
310-
let upload_button = self.view.button(ids!(wrapper.buttons_view.upload_button));
311-
upload_button.reset_hover(cx);
312-
upload_button.set_enabled(cx, true);
313-
314-
// Store the file data for later upload, clone only once
315-
let cloned_file_data = file_data.clone();
316-
self.set_content(cx, cloned_file_data.clone());
317-
self.file_data = Some(cloned_file_data);
278+
self.set_content(cx, file_data.clone());
279+
self.file_meta = Some(file_data.0.clone());
318280
continue;
319281
}
320282
}
@@ -413,7 +375,7 @@ pub enum FileType {
413375
///
414376
/// Uses binary units (1024 bytes = 1 KB) for conversion.
415377
/// For sizes less than 1 KB, displays the exact byte count without decimal places.
416-
fn format_file_size(bytes: u64) -> String {
378+
pub fn format_file_size(bytes: u64) -> String {
417379
const UNITS: &[&str] = &["B", "KB", "MB", "GB", "TB"];
418380

419381
if bytes == 0 {
@@ -442,5 +404,7 @@ pub struct FilePreviewerMetaData {
442404
pub filename: String,
443405
pub mime: Mime,
444406
/// File size in bytes
445-
pub file_size: usize,
407+
pub file_size: u64,
408+
/// Path to the original file
409+
pub file_path: std::path::PathBuf,
446410
}

src/shared/progress.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ live_design! {
1111

1212
// A horizontal progress bar with rounded ends (capsule shape).
1313
// Displays progress from 0% to 100% with a colored fill.
14-
pub MyProgress = {{MyProgress}} {
14+
pub Progress = {{Progress}} {
1515
width: Fill,
1616
height: 8,
1717

@@ -55,7 +55,7 @@ live_design! {
5555

5656
/// A horizontal progress bar widget that displays a percentage value (0-100).
5757
#[derive(Live, LiveHook, Widget)]
58-
pub struct MyProgress {
58+
pub struct Progress {
5959
#[redraw]
6060
#[live]
6161
draw_bg: DrawQuad,
@@ -67,7 +67,7 @@ pub struct MyProgress {
6767
value: f64,
6868
}
6969

70-
impl Widget for MyProgress {
70+
impl Widget for Progress {
7171
fn handle_event(&mut self, _cx: &mut Cx, _event: &Event, _scope: &mut Scope) {}
7272

7373
fn draw_walk(&mut self, cx: &mut Cx2d, _scope: &mut Scope, walk: Walk) -> DrawStep {
@@ -85,7 +85,7 @@ impl Widget for MyProgress {
8585
}
8686
}
8787

88-
impl MyProgress {
88+
impl Progress {
8989
/// Returns the current progress value as a percentage (0.0-100.0).
9090
pub fn value(&self) -> f64 {
9191
self.value
@@ -99,7 +99,7 @@ impl MyProgress {
9999
}
100100
}
101101

102-
impl MyProgressRef {
102+
impl ProgressRef {
103103
/// Returns the current progress value as a percentage (0.0-100.0).
104104
/// Returns 0.0 if the widget reference is invalid.
105105
pub fn value(&self) -> f64 {

0 commit comments

Comments
 (0)