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+
1920use matrix_sdk:: room:: reply:: { EnforceThread , Reply } ;
2021use matrix_sdk_ui:: timeline:: { EmbeddedEvent , EventTimelineItem , TimelineEventItemId } ;
2122use 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} ;
2324use crate :: shared:: file_previewer:: FilePreviewerAction ;
2425
2526live_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
224227impl 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