@@ -19,9 +19,13 @@ use makepad_widgets::*;
1919use matrix_sdk:: room:: reply:: { EnforceThread , Reply } ;
2020use matrix_sdk_ui:: timeline:: { EmbeddedEvent , EventTimelineItem , TimelineEventItemId } ;
2121use 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 , file_previewer:: { FileLoadReceiver , FilePreviewerMetaData } , html_or_plaintext:: HtmlOrPlaintextWidgetRefExt , mentionable_text_input:: MentionableTextInputWidgetExt , popup_list:: { PopupItem , PopupKind , enqueue_popup_notification} , progress:: ProgressWidgetExt , styles:: * } , sliding_sync:: { MatrixRequest , UserPowerLevels , submit_async_request} , utils} ;
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 , file_previewer:: { FileLoadReceiver , FilePreviewerMetaData , format_file_size } , html_or_plaintext:: HtmlOrPlaintextWidgetRefExt , mentionable_text_input:: MentionableTextInputWidgetExt , popup_list:: { PopupItem , PopupKind , enqueue_popup_notification} , progress:: ProgressWidgetExt , styles:: * } , sliding_sync:: { MatrixRequest , UserPowerLevels , submit_async_request} , utils} ;
2323use crate :: shared:: file_previewer:: FilePreviewerAction ;
2424
25+ /// Maximum file size allowed for upload (100 MB).
26+ /// Files larger than this will be rejected to prevent memory issues.
27+ const MAX_FILE_SIZE_BYTES : u64 = 100 * 1024 * 1024 ;
28+
2529live_design ! {
2630 use link:: theme:: * ;
2731 use link:: shaders:: * ;
@@ -80,14 +84,36 @@ live_design! {
8084 flow: Down ,
8185 spacing: 5 ,
8286
83- progress_label = <Label > {
87+ header_row = <View > {
8488 width: Fill ,
8589 height: Fit ,
86- draw_text: {
87- text_style: <REGULAR_TEXT >{ font_size: 10 } ,
88- color: #666
90+ flow: Right ,
91+ align: { y: 0.5 }
92+ spacing: 10 ,
93+
94+ progress_label = <Label > {
95+ width: Fill ,
96+ height: Fit ,
97+ draw_text: {
98+ text_style: <REGULAR_TEXT >{ font_size: 10 } ,
99+ color: #666
100+ }
101+ text: "Uploading..."
102+ }
103+
104+ cancel_upload_button = <Button > {
105+ width: Fit ,
106+ height: Fit ,
107+ padding: { top: 4 , bottom: 4 , left: 8 , right: 8 }
108+ draw_text: {
109+ text_style: <REGULAR_TEXT >{ font_size: 9 } ,
110+ color: #fff
111+ }
112+ draw_bg: {
113+ color: #c44
114+ }
115+ text: "Cancel"
89116 }
90- text: "Uploading..."
91117 }
92118
93119 progress = <Progress > {
@@ -269,8 +295,8 @@ impl Widget for RoomInputBar {
269295 0.0
270296 } ;
271297
272- self . view . progress ( ids ! ( progress) ) . set_value ( cx, progress_percentage) ;
273- let progress_label = self . view . label ( ids ! ( upload_progress_view. progress_label) ) ;
298+ self . view . progress ( ids ! ( upload_progress_view . progress) ) . set_value ( cx, progress_percentage) ;
299+ let progress_label = self . view . label ( ids ! ( upload_progress_view. header_row . progress_label) ) ;
274300 progress_label. set_text ( cx, & format ! ( "Uploading... {:.0}%" , progress_percentage) ) ;
275301 }
276302 self . redraw ( cx) ;
@@ -339,6 +365,44 @@ impl RoomInputBar {
339365 // Handle the file attachment upload button being clicked.
340366 if self . button ( ids ! ( attachment_upload_button) ) . clicked ( actions) {
341367 if let Some ( selected_file_path) = rfd:: FileDialog :: new ( ) . pick_file ( ) {
368+ // Check file size before reading to prevent memory issues
369+ let file_size = match std:: fs:: metadata ( & selected_file_path) {
370+ Ok ( metadata) => metadata. len ( ) ,
371+ Err ( e) => {
372+ error ! ( "Failed to read file metadata for {:?}: {}" , selected_file_path, e) ;
373+ enqueue_popup_notification ( PopupItem {
374+ message : format ! ( "Unable to access file: {}" , e) ,
375+ auto_dismissal_duration : None ,
376+ kind : PopupKind :: Error
377+ } ) ;
378+ return ;
379+ }
380+ } ;
381+
382+ // Check for empty files
383+ if file_size == 0 {
384+ enqueue_popup_notification ( PopupItem {
385+ message : String :: from ( "Cannot upload empty file" ) ,
386+ auto_dismissal_duration : None ,
387+ kind : PopupKind :: Error
388+ } ) ;
389+ return ;
390+ }
391+
392+ // Check if file exceeds maximum size
393+ if file_size > MAX_FILE_SIZE_BYTES {
394+ enqueue_popup_notification ( PopupItem {
395+ message : format ! (
396+ "File too large ({}). Maximum allowed size is {}" ,
397+ format_file_size( file_size) ,
398+ format_file_size( MAX_FILE_SIZE_BYTES )
399+ ) ,
400+ auto_dismissal_duration : None ,
401+ kind : PopupKind :: Error
402+ } ) ;
403+ return ;
404+ }
405+
342406 let filename = selected_file_path
343407 . file_name ( )
344408 . and_then ( |n| n. to_str ( ) )
@@ -355,7 +419,6 @@ impl RoomInputBar {
355419 let ( sender, receiver) = std:: sync:: mpsc:: channel ( ) ;
356420 self . background_task_id = self . background_task_id . wrapping_add ( 1 ) ;
357421 self . receiver = Some ( ( self . background_task_id , receiver) ) ;
358-
359422 // Read file in background thread to avoid blocking the UI
360423 cx. spawn_thread ( move || {
361424 match std:: fs:: read ( & selected_file_path) {
@@ -383,6 +446,21 @@ impl RoomInputBar {
383446 }
384447 }
385448
449+ // Handle cancel upload button being clicked.
450+ if self . button ( ids ! ( upload_progress_view. header_row. cancel_upload_button) ) . clicked ( actions) {
451+ log ! ( "Upload cancelled by user" ) ;
452+ // Hide the progress bar immediately
453+ self . view . view ( ids ! ( upload_progress_view) ) . set_visible ( cx, false ) ;
454+ self . upload_progress_subscriber = None ;
455+ // Note: The actual upload will continue in the background but user won't see progress
456+ enqueue_popup_notification ( PopupItem {
457+ message : String :: from ( "Upload cancelled" ) ,
458+ kind : PopupKind :: Info ,
459+ auto_dismissal_duration : Some ( 3.0 )
460+ } ) ;
461+ self . redraw ( cx) ;
462+ }
463+
386464 // Handle the send location button being clicked.
387465 if self . button ( ids ! ( location_preview. send_location_button) ) . clicked ( actions) {
388466 let location_preview = self . location_preview ( ids ! ( location_preview) ) ;
0 commit comments