Skip to content

Commit 0ca00fb

Browse files
committed
image upload improvement
1 parent bdb0b34 commit 0ca00fb

5 files changed

Lines changed: 251 additions & 253 deletions

File tree

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ bytesize = "2.0"
5757
bitflags = "2.6.0"
5858
indexmap = "2.6.0"
5959
blurhash = { version = "0.2.3", default-features = false }
60+
mime_guess = "2.0.5"
61+
rfd = { version = "0.15.3", features = ["ashpd", "urlencoding", "xdg-portal"] }
6062

6163
## Dependencies for TSP support.
6264
## Commit "f0bc4625dcd729e07e4a36257df2f1d94c81cef4" is the most recent one without the invalid change to pin serde to 1.0.219.

src/room/room_input_bar.rs

Lines changed: 81 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,12 @@
1515
//! * A "cannot-send-message" notice, which is shown if the user cannot send messages to the room.
1616
//!
1717
18-
use makepad_widgets::{file_dialogs::FileDialog, *};
18+
use makepad_widgets::*;
19+
1920
use matrix_sdk::room::reply::{EnforceThread, Reply};
2021
use matrix_sdk_ui::timeline::{EmbeddedEvent, EventTimelineItem, TimelineEventItemId};
2122
use ruma::{events::room::message::{LocationMessageEventContent, MessageType, RoomMessageEventContent}, OwnedRoomId};
22-
use crate::{home::{editing_pane::{EditingPaneState, EditingPaneWidgetExt}, location_preview::LocationPreviewWidgetExt, room_screen::{MessageAction, RoomScreenProps, populate_preview_of_timeline_item}, tombstone_footer::{SuccessorRoomDetails, TombstoneFooterWidgetExt}}, location::init_location_subscriber, shared::{avatar::AvatarWidgetRefExt, html_or_plaintext::HtmlOrPlaintextWidgetRefExt, mentionable_text_input::MentionableTextInputWidgetExt, popup_list::{PopupItem, PopupKind, enqueue_popup_notification}, progress::MyProgressWidgetExt, styles::*}, sliding_sync::{MatrixRequest, UserPowerLevels, submit_async_request}, utils};
23+
use crate::{home::{editing_pane::{EditingPaneState, EditingPaneWidgetExt}, location_preview::LocationPreviewWidgetExt, room_screen::{MessageAction, RoomScreenProps, populate_preview_of_timeline_item}, tombstone_footer::{SuccessorRoomDetails, TombstoneFooterWidgetExt}}, location::init_location_subscriber, shared::{avatar::AvatarWidgetRefExt, file_previewer::{FileLoadReceiver, FilePreviewerMetaData}, html_or_plaintext::HtmlOrPlaintextWidgetRefExt, mentionable_text_input::MentionableTextInputWidgetExt, popup_list::{PopupItem, PopupKind, enqueue_popup_notification}, progress::MyProgressWidgetExt, styles::*}, sliding_sync::{MatrixRequest, UserPowerLevels, submit_async_request}, utils};
2324
use crate::shared::file_previewer::FilePreviewerAction;
2425

2526
live_design! {
@@ -139,7 +140,7 @@ live_design! {
139140
draw_bg: {
140141
color: (COLOR_PRIMARY),
141142
}
142-
icon_walk: {width: Fit, height: 23, margin: {bottom: -1}}
143+
icon_walk: {width: 20, height: 23, margin: {bottom: -1, left: 5}}
143144
text: "",
144145
}
145146

@@ -218,7 +219,9 @@ pub struct RoomInputBar {
218219
/// Info about the message event that the user is currently replying to, if any.
219220
#[rust] replying_to: Option<(EventTimelineItem, EmbeddedEvent)>,
220221
/// Subscriber for upload progress updates
221-
#[rust] upload_progress_subscriber: Option<eyeball::Subscriber<matrix_sdk::TransmissionProgress>>
222+
#[rust] upload_progress_subscriber: Option<eyeball::Subscriber<matrix_sdk::TransmissionProgress>>,
223+
#[rust] background_task_id: u32,
224+
#[rust] receiver: Option<(u32, FileLoadReceiver)>,
222225
}
223226

224227
impl Widget for RoomInputBar {
@@ -249,45 +252,49 @@ impl Widget for RoomInputBar {
249252
}
250253
_ => {}
251254
}
252-
if let Event::FileDialogResult { live_id, path } = event {
253-
if *live_id == live_id!(attachment_upload) {
254-
if let Some(path) = path {
255-
log!("File selected for upload: {}", path.display());
256-
// Post action to show the file previewer modal
257-
cx.action(FilePreviewerAction::Show {
258-
file_path: path.clone(),
259-
});
260-
}
261-
}
262-
}
263255

264-
// Handle upload progress updates
256+
// Update upload progress display
265257
if let Some(subscriber) = &self.upload_progress_subscriber {
266-
// Get the current progress value
267258
let progress = subscriber.get();
268259
if progress.current >= progress.total {
269-
// Upload complete, hide progress bar
260+
// Upload complete, hide the progress bar
270261
self.view.view(ids!(upload_progress_view)).set_visible(cx, false);
271262
self.upload_progress_subscriber = None;
272263
} else {
273264
self.view.view(ids!(upload_progress_view)).set_visible(cx, true);
274-
// Update progress bar width as a percentage
275-
let progress_val = if progress.total > 0 {
265+
266+
// Calculate progress percentage, avoiding division by zero
267+
let progress_percentage = if progress.total > 0 {
276268
(progress.current as f64 / progress.total as f64) * 100.0
277269
} else {
278270
0.0
279271
};
280-
self.view.my_progress(ids!(progress)).set_value(cx, progress_val);
281272

273+
self.view.my_progress(ids!(progress)).set_value(cx, progress_percentage);
282274
let progress_label = self.view.label(ids!(upload_progress_view.progress_label));
283-
progress_label.set_text(cx, &format!("Uploading... {:.0}%", progress_val));
275+
progress_label.set_text(cx, &format!("Uploading... {:.0}%", progress_percentage));
284276
}
285277
self.redraw(cx);
286278
}
287279
if let Event::Actions(actions) = event {
288280
self.handle_actions(cx, actions, room_screen_props);
289281
}
290-
282+
if let (Event::Signal, Some((_background_task_id, receiver))) = (event, &mut self.receiver) {
283+
let mut remove_receiver = false;
284+
match receiver.try_recv() {
285+
Ok(file) => {
286+
cx.action(FilePreviewerAction::Show(file));
287+
remove_receiver = true;
288+
}
289+
Err(std::sync::mpsc::TryRecvError::Empty) => {}
290+
Err(std::sync::mpsc::TryRecvError::Disconnected) => {
291+
remove_receiver = true;
292+
}
293+
}
294+
if remove_receiver {
295+
self.receiver = None;
296+
}
297+
}
291298
self.view.handle_event(cx, event, scope);
292299
}
293300

@@ -330,9 +337,51 @@ impl RoomInputBar {
330337
self.redraw(cx);
331338
}
332339

333-
// Handle the image upload button being clicked.
340+
// Handle the file attachment upload button being clicked.
334341
if self.button(ids!(attachment_upload_button)).clicked(actions) {
335-
cx.open_system_openfile_dialog(live_id!(attachment_upload), FileDialog::new());
342+
if let Some(selected_file_path) = rfd::FileDialog::new().pick_file() {
343+
let filename = selected_file_path
344+
.file_name()
345+
.and_then(|n| n.to_str())
346+
.unwrap_or("unknown")
347+
.to_string();
348+
349+
// Detect the MIME type from the file extension
350+
let mime_str = mime_guess::from_path(&selected_file_path)
351+
.first_or_octet_stream()
352+
.to_string();
353+
use mime_guess::mime;
354+
let mime: mime::Mime = mime_str.parse().unwrap_or(mime::APPLICATION_OCTET_STREAM);
355+
356+
let (sender, receiver) = std::sync::mpsc::channel();
357+
self.background_task_id = self.background_task_id.wrapping_add(1);
358+
self.receiver = Some((self.background_task_id, receiver));
359+
360+
// Read file in background thread to avoid blocking the UI
361+
cx.spawn_thread(move || {
362+
match std::fs::read(&selected_file_path) {
363+
Ok(file_data) => {
364+
let metadata = FilePreviewerMetaData {
365+
filename,
366+
mime,
367+
file_size: file_data.len(),
368+
};
369+
if sender.send((metadata, file_data)).is_err() {
370+
error!("Failed to send file data to UI: receiver dropped");
371+
}
372+
}
373+
Err(read_error) => {
374+
error!("Failed to read file {:?}: {}", selected_file_path, read_error);
375+
enqueue_popup_notification(PopupItem {
376+
message: format!("Unable to read the file: {}", read_error),
377+
auto_dismissal_duration: None,
378+
kind: PopupKind::Error
379+
});
380+
}
381+
}
382+
SignalToUI::set_ui_signal();
383+
});
384+
}
336385
}
337386

338387
// Handle the send location button being clicked.
@@ -429,11 +478,11 @@ impl RoomInputBar {
429478
self.on_editing_pane_hidden(cx);
430479
}
431480

432-
// Handle file upload action
481+
// Handle file upload confirmation from the file previewer
433482
for action in actions {
434-
if let Some(FilePreviewerAction::Upload { file_path }) = action.downcast_ref() {
435-
// Reconstruct the Reply from the event_id
436-
let replied_to = self.replying_to.take().and_then(|(event_tl_item, _emb)|
483+
if let Some(FilePreviewerAction::Upload(file_load_data)) = action.downcast_ref() {
484+
// If replying to a message, construct the reply metadata
485+
let replied_to = self.replying_to.take().and_then(|(event_tl_item, _embedded_event)|
437486
event_tl_item.event_id().map(|event_id|
438487
Reply {
439488
event_id: event_id.to_owned(),
@@ -442,17 +491,17 @@ impl RoomInputBar {
442491
)
443492
);
444493

445-
// Create a SharedObservable for tracking upload progress
494+
// Set up progress tracking for the upload
446495
use matrix_sdk::TransmissionProgress;
447496
let progress_observable = eyeball::SharedObservable::new(TransmissionProgress::default());
448497
let progress_subscriber = progress_observable.subscribe();
449498

450-
// Store the subscriber so we can track progress updates
451499
self.upload_progress_subscriber = Some(progress_subscriber);
452500
progress_observable.set(TransmissionProgress { current: 0, total: 100 });
501+
453502
submit_async_request(MatrixRequest::Upload {
454503
room_id: room_screen_props.room_name_id.room_id().clone(),
455-
file_path: file_path.clone(),
504+
file_data: file_load_data.clone(),
456505
replied_to,
457506
#[cfg(feature = "tsp")]
458507
sign_with_tsp: false,

0 commit comments

Comments
 (0)