Fix sync status display issues
b199364 parent: e1b5362 modified
src/config/sync_task.rs +38 -5 | @@ -172,6 +172,13 @@ impl SyncStatus { | ||
| 172 | 172 | self.global_sync_in_progress || |
| 173 | 173 | self.feed_sync_status.values().any(|s| matches!(s, FeedSyncStatus::InProgress { .. })) |
| 174 | 174 | } |
| 175 | + | |
| 176 | + /// Check if there are any sync activities (in progress or recently completed) | |
| 177 | + /// Used to determine if we should show sync-related UI elements | |
| 178 | + pub fn has_sync_activity(&self) -> bool { | |
| 179 | + self.global_sync_in_progress || | |
| 180 | + !self.feed_sync_status.is_empty() | |
| 181 | + } | |
| 175 | 182 | |
| 176 | 183 | /// Get syncing feed count |
| 177 | 184 | pub fn syncing_feed_count(&self) -> usize { |
| @@ -236,11 +243,6 @@ impl SyncTaskManager { | ||
| 236 | 243 | ); |
| 237 | 244 | } |
| 238 | 245 | SyncMessage::SyncProgress { feed_id, progress, message } => { |
| 239 | - if let Some(status) = self.status.feed_sync_status.get_mut(feed_id) { | |
| 240 | - if let FeedSyncStatus::InProgress { progress: _, message: _ } = status { | |
| 241 | - // Update existing in-progress status | |
| 242 | - } | |
| 243 | - } | |
| 244 | 246 | self.status.update_feed_status( |
| 245 | 247 | feed_id.clone(), |
| 246 | 248 | FeedSyncStatus::InProgress { |
| @@ -257,6 +259,16 @@ impl SyncTaskManager { | ||
| 257 | 259 | episodes_downloaded: *episodes_downloaded, |
| 258 | 260 | } |
| 259 | 261 | ); |
| 262 | + | |
| 263 | + // Check if all feeds are completed | |
| 264 | + if self.status.global_sync_in_progress { | |
| 265 | + let all_completed = self.status.feed_sync_status.values().all(|s| { | |
| 266 | + matches!(s, FeedSyncStatus::Completed { .. } | FeedSyncStatus::Failed { .. }) | |
| 267 | + }); | |
| 268 | + if all_completed { | |
| 269 | + self.status.complete_global_sync(); | |
| 270 | + } | |
| 271 | + } | |
| 260 | 272 | } |
| 261 | 273 | SyncMessage::SyncFailed { feed_id, feed_name: _, error } => { |
| 262 | 274 | self.status.update_feed_status( |
| @@ -265,6 +277,20 @@ impl SyncTaskManager { | ||
| 265 | 277 | error: error.clone(), |
| 266 | 278 | } |
| 267 | 279 | ); |
| 280 | + | |
| 281 | + // Check if all feeds are completed (including failed ones) | |
| 282 | + if self.status.global_sync_in_progress { | |
| 283 | + let all_completed = self.status.feed_sync_status.values().all(|s| { | |
| 284 | + matches!(s, FeedSyncStatus::Completed { .. } | FeedSyncStatus::Failed { .. }) | |
| 285 | + }); | |
| 286 | + if all_completed { | |
| 287 | + self.status.complete_global_sync(); | |
| 288 | + } | |
| 289 | + } | |
| 290 | + | |
| 291 | + // For single feed sync, we should also clean up after a failure | |
| 292 | + // Remove the feed from tracking after a reasonable delay | |
| 293 | + // This is handled by the is_syncing() check which looks at InProgress status | |
| 268 | 294 | } |
| 269 | 295 | SyncMessage::EpisodeDownloadStarted { feed_id: _, episode_title: _ } => { |
| 270 | 296 | // Episode download started - could track this if needed |
| @@ -310,6 +336,13 @@ impl SyncTaskManager { | ||
| 310 | 336 | crate::rss::async_sync::sync_feed_async(feed, sender); |
| 311 | 337 | }); |
| 312 | 338 | } |
| 339 | + | |
| 340 | + /// Clears completed and failed sync statuses | |
| 341 | + pub fn clear_completed_sync_status(&mut self) { | |
| 342 | + self.status.feed_sync_status.retain(|_, status| { | |
| 343 | + matches!(status, FeedSyncStatus::InProgress { .. }) | |
| 344 | + }); | |
| 345 | + } | |
| 313 | 346 | } |
| 314 | 347 | |
| 315 | 348 | impl Default for SyncTaskManager { |
| @@ -172,6 +172,13 @@ impl SyncStatus { | |||
| 172 | self.global_sync_in_progress || | 172 | self.global_sync_in_progress || |
| 173 | self.feed_sync_status.values().any(|s| matches!(s, FeedSyncStatus::InProgress { .. })) | 173 | self.feed_sync_status.values().any(|s| matches!(s, FeedSyncStatus::InProgress { .. })) |
| 174 | } | 174 | } |
| 175 | + | ||
| 176 | + /// Check if there are any sync activities (in progress or recently completed) | ||
| 177 | + /// Used to determine if we should show sync-related UI elements | ||
| 178 | + pub fn has_sync_activity(&self) -> bool { | ||
| 179 | + self.global_sync_in_progress || | ||
| 180 | + !self.feed_sync_status.is_empty() | ||
| 181 | + } | ||
| 175 | 182 | ||
| 176 | /// Get syncing feed count | 183 | /// Get syncing feed count |
| 177 | pub fn syncing_feed_count(&self) -> usize { | 184 | pub fn syncing_feed_count(&self) -> usize { |
| @@ -236,11 +243,6 @@ impl SyncTaskManager { | |||
| 236 | ); | 243 | ); |
| 237 | } | 244 | } |
| 238 | SyncMessage::SyncProgress { feed_id, progress, message } => { | 245 | SyncMessage::SyncProgress { feed_id, progress, message } => { |
| 239 | - if let Some(status) = self.status.feed_sync_status.get_mut(feed_id) { | ||
| 240 | - if let FeedSyncStatus::InProgress { progress: _, message: _ } = status { | ||
| 241 | - // Update existing in-progress status | ||
| 242 | - } | ||
| 243 | - } | ||
| 244 | self.status.update_feed_status( | 246 | self.status.update_feed_status( |
| 245 | feed_id.clone(), | 247 | feed_id.clone(), |
| 246 | FeedSyncStatus::InProgress { | 248 | FeedSyncStatus::InProgress { |
| @@ -257,6 +259,16 @@ impl SyncTaskManager { | |||
| 257 | episodes_downloaded: *episodes_downloaded, | 259 | episodes_downloaded: *episodes_downloaded, |
| 258 | } | 260 | } |
| 259 | ); | 261 | ); |
| 262 | + | ||
| 263 | + // Check if all feeds are completed | ||
| 264 | + if self.status.global_sync_in_progress { | ||
| 265 | + let all_completed = self.status.feed_sync_status.values().all(|s| { | ||
| 266 | + matches!(s, FeedSyncStatus::Completed { .. } | FeedSyncStatus::Failed { .. }) | ||
| 267 | + }); | ||
| 268 | + if all_completed { | ||
| 269 | + self.status.complete_global_sync(); | ||
| 270 | + } | ||
| 271 | + } | ||
| 260 | } | 272 | } |
| 261 | SyncMessage::SyncFailed { feed_id, feed_name: _, error } => { | 273 | SyncMessage::SyncFailed { feed_id, feed_name: _, error } => { |
| 262 | self.status.update_feed_status( | 274 | self.status.update_feed_status( |
| @@ -265,6 +277,20 @@ impl SyncTaskManager { | |||
| 265 | error: error.clone(), | 277 | error: error.clone(), |
| 266 | } | 278 | } |
| 267 | ); | 279 | ); |
| 280 | + | ||
| 281 | + // Check if all feeds are completed (including failed ones) | ||
| 282 | + if self.status.global_sync_in_progress { | ||
| 283 | + let all_completed = self.status.feed_sync_status.values().all(|s| { | ||
| 284 | + matches!(s, FeedSyncStatus::Completed { .. } | FeedSyncStatus::Failed { .. }) | ||
| 285 | + }); | ||
| 286 | + if all_completed { | ||
| 287 | + self.status.complete_global_sync(); | ||
| 288 | + } | ||
| 289 | + } | ||
| 290 | + | ||
| 291 | + // For single feed sync, we should also clean up after a failure | ||
| 292 | + // Remove the feed from tracking after a reasonable delay | ||
| 293 | + // This is handled by the is_syncing() check which looks at InProgress status | ||
| 268 | } | 294 | } |
| 269 | SyncMessage::EpisodeDownloadStarted { feed_id: _, episode_title: _ } => { | 295 | SyncMessage::EpisodeDownloadStarted { feed_id: _, episode_title: _ } => { |
| 270 | // Episode download started - could track this if needed | 296 | // Episode download started - could track this if needed |
| @@ -310,6 +336,13 @@ impl SyncTaskManager { | |||
| 310 | crate::rss::async_sync::sync_feed_async(feed, sender); | 336 | crate::rss::async_sync::sync_feed_async(feed, sender); |
| 311 | }); | 337 | }); |
| 312 | } | 338 | } |
| 339 | + | ||
| 340 | + /// Clears completed and failed sync statuses | ||
| 341 | + pub fn clear_completed_sync_status(&mut self) { | ||
| 342 | + self.status.feed_sync_status.retain(|_, status| { | ||
| 343 | + matches!(status, FeedSyncStatus::InProgress { .. }) | ||
| 344 | + }); | ||
| 345 | + } | ||
| 313 | } | 346 | } |
| 314 | 347 | ||
| 315 | impl Default for SyncTaskManager { | 348 | impl Default for SyncTaskManager { |
modified
src/ui/home.rs +0 -3 | @@ -33,9 +33,6 @@ pub fn draw_home(state: &mut AppState, ui: &mut Ui) { | ||
| 33 | 33 | draw_action_buttons(state, ui); |
| 34 | 34 | }); |
| 35 | 35 | |
| 36 | - // Show sync status panel | |
| 37 | - crate::ui::sync_status::draw_sync_status_panel(state, ui); | |
| 38 | - | |
| 39 | 36 | ui.add_space(16.0); |
| 40 | 37 | ui.separator(); |
| 41 | 38 | |
| @@ -33,9 +33,6 @@ pub fn draw_home(state: &mut AppState, ui: &mut Ui) { | |||
| 33 | draw_action_buttons(state, ui); | 33 | draw_action_buttons(state, ui); |
| 34 | }); | 34 | }); |
| 35 | 35 | ||
| 36 | - // Show sync status panel | ||
| 37 | - crate::ui::sync_status::draw_sync_status_panel(state, ui); | ||
| 38 | - | ||
| 39 | ui.add_space(16.0); | 36 | ui.add_space(16.0); |
| 40 | ui.separator(); | 37 | ui.separator(); |
| 41 | 38 | ||
modified
src/ui/status_bar.rs +65 -66 | @@ -29,80 +29,79 @@ pub fn draw_status_bar(state: &mut AppState, ui: &mut Ui) { | ||
| 29 | 29 | ..Default::default() |
| 30 | 30 | } |
| 31 | 31 | .show(ui, |ui| { |
| 32 | - ui.spacing_mut().item_spacing.x = 16.0; | |
| 32 | + ui.spacing_mut().item_spacing.x = 16.0; | |
| 33 | 33 | |
| 34 | - // Error message (highest priority) avec icône | |
| 35 | - if let Some(ref error) = state.error_message { | |
| 36 | - let error_clone = error.clone(); | |
| 37 | - ui.horizontal(|ui| { | |
| 38 | - ui.label(egui::RichText::new("❌ ").color(egui::Color32::RED)); | |
| 39 | - ui.colored_label(egui::Color32::from_rgb(240, 100, 100), &error_clone); | |
| 40 | - if ui.button(egui::RichText::new("✕").size(14.0)).clicked() { | |
| 41 | - state.clear_error(); | |
| 42 | - } | |
| 43 | - }); | |
| 44 | - return; | |
| 45 | - } | |
| 34 | + // Error message (highest priority) avec icône | |
| 35 | + if let Some(ref error) = state.error_message { | |
| 36 | + let error_clone = error.clone(); | |
| 37 | + ui.horizontal(|ui| { | |
| 38 | + ui.label(egui::RichText::new("❌ ").color(egui::Color32::RED)); | |
| 39 | + ui.colored_label(egui::Color32::from_rgb(240, 100, 100), &error_clone); | |
| 40 | + if ui.button(egui::RichText::new("✕").size(14.0)).clicked() { | |
| 41 | + state.clear_error(); | |
| 42 | + } | |
| 43 | + }); | |
| 44 | + return; | |
| 45 | + } | |
| 46 | 46 | |
| 47 | - // Info message avec icône | |
| 48 | - if let Some(ref info) = state.info_message { | |
| 49 | - let info_clone = info.clone(); | |
| 50 | - ui.horizontal(|ui| { | |
| 51 | - ui.label(egui::RichText::new("ℹ️ ").color(egui::Color32::from_rgb(100, 200, 100))); | |
| 52 | - ui.colored_label(egui::Color32::from_rgb(100, 220, 100), &info_clone); | |
| 53 | - if ui.button(egui::RichText::new("✕").size(14.0)).clicked() { | |
| 54 | - state.clear_info(); | |
| 55 | - } | |
| 56 | - }); | |
| 57 | - return; | |
| 58 | - } | |
| 47 | + // Info message avec icône | |
| 48 | + if let Some(ref info) = state.info_message { | |
| 49 | + let info_clone = info.clone(); | |
| 50 | + ui.horizontal(|ui| { | |
| 51 | + ui.label(egui::RichText::new("ℹ️ ").color(egui::Color32::from_rgb(100, 200, 100))); | |
| 52 | + ui.colored_label(egui::Color32::from_rgb(100, 220, 100), &info_clone); | |
| 53 | + if ui.button(egui::RichText::new("✕").size(14.0)).clicked() { | |
| 54 | + state.clear_info(); | |
| 55 | + } | |
| 56 | + }); | |
| 57 | + return; | |
| 58 | + } | |
| 59 | 59 | |
| 60 | - // Sync status avec icône | |
| 61 | - let feed_count = state.feed_count(); | |
| 62 | - let enabled_count = state.enabled_feed_count(); | |
| 60 | + // Sync status avec icône | |
| 61 | + let feed_count = state.feed_count(); | |
| 62 | + let enabled_count = state.enabled_feed_count(); | |
| 63 | + | |
| 64 | + // Process sync messages | |
| 65 | + state.sync_task_manager.process_messages(); | |
| 66 | + | |
| 67 | + ui.horizontal(|ui| { | |
| 68 | + // Use the sync indicator from sync_status module | |
| 69 | + crate::ui::sync_status::draw_sync_indicator(state, ui); | |
| 63 | 70 | |
| 64 | - // Process sync messages and get status | |
| 65 | - let is_syncing = state.sync_task_manager.status.is_syncing(); | |
| 66 | - let global_sync_in_progress = state.sync_task_manager.status.global_sync_in_progress; | |
| 71 | + // Show ready status only if no sync activity at all | |
| 72 | + if !state.sync_task_manager.status.has_sync_activity() { | |
| 73 | + ui.label(egui::RichText::new("✅ ").color(egui::Color32::from_rgb(100, 220, 100))); | |
| 74 | + ui.colored_label(egui::Color32::from_rgb(150, 200, 150), "Prêt"); | |
| 75 | + } | |
| 76 | + | |
| 77 | + // Theme toggle button | |
| 78 | + let theme_icon = match state.get_theme() { | |
| 79 | + Theme::Dark => "🌙", | |
| 80 | + Theme::Light => "☀️", | |
| 81 | + }; | |
| 82 | + let theme_tooltip = match state.get_theme() { | |
| 83 | + Theme::Dark => "Basculer vers le thème clair", | |
| 84 | + Theme::Light => "Basculer vers le thème sombre", | |
| 85 | + }; | |
| 67 | 86 | |
| 68 | - ui.horizontal(|ui| { | |
| 69 | - // Use the sync indicator from sync_status module | |
| 70 | - crate::ui::sync_status::draw_sync_indicator(state, ui); | |
| 71 | - | |
| 72 | - // Show ready status if no sync in progress | |
| 73 | - if !is_syncing && !global_sync_in_progress { | |
| 74 | - ui.label(egui::RichText::new("✅ ").color(egui::Color32::from_rgb(100, 220, 100))); | |
| 75 | - ui.colored_label(egui::Color32::from_rgb(150, 200, 150), "Prêt"); | |
| 76 | - } | |
| 87 | + if ui.link(egui::RichText::new(theme_icon).size(16.0)) | |
| 88 | + .on_hover_text(theme_tooltip) | |
| 89 | + .clicked() | |
| 90 | + { | |
| 91 | + state.toggle_theme(); | |
| 92 | + } | |
| 77 | 93 | |
| 78 | - // Theme toggle button | |
| 79 | - let theme_icon = match state.get_theme() { | |
| 80 | - Theme::Dark => "🌙", | |
| 81 | - Theme::Light => "☀️", | |
| 82 | - }; | |
| 83 | - let theme_tooltip = match state.get_theme() { | |
| 84 | - Theme::Dark => "Basculer vers le thème clair", | |
| 85 | - Theme::Light => "Basculer vers le thème sombre", | |
| 86 | - }; | |
| 94 | + // Feed count on the right avec icône | |
| 95 | + ui.with_layout(Layout::right_to_left(Align::Center), |ui| { | |
| 96 | + ui.label(egui::RichText::new(format!("📊 {} flux ", feed_count)) | |
| 97 | + .color(egui::Color32::from_rgb(160, 180, 200))); | |
| 87 | 98 | |
| 88 | - if ui.link(egui::RichText::new(theme_icon).size(16.0)) | |
| 89 | - .on_hover_text(theme_tooltip) | |
| 90 | - .clicked() | |
| 91 | - { | |
| 92 | - state.toggle_theme(); | |
| 99 | + // Nombre de flux activés | |
| 100 | + if enabled_count < feed_count { | |
| 101 | + ui.label(egui::RichText::new(format!("| {} activés", enabled_count)) | |
| 102 | + .color(egui::Color32::from_rgb(140, 160, 180))); | |
| 93 | 103 | } |
| 94 | - | |
| 95 | - // Feed count on the right avec icône | |
| 96 | - ui.with_layout(Layout::right_to_left(Align::Center), |ui| { | |
| 97 | - ui.label(egui::RichText::new(format!("📊 {} flux ", feed_count)) | |
| 98 | - .color(egui::Color32::from_rgb(160, 180, 200))); | |
| 99 | - | |
| 100 | - // Nombre de flux activés | |
| 101 | - if enabled_count < feed_count { | |
| 102 | - ui.label(egui::RichText::new(format!("| {} activés", enabled_count)) | |
| 103 | - .color(egui::Color32::from_rgb(140, 160, 180))); | |
| 104 | - } | |
| 105 | - }); | |
| 106 | 104 | }); |
| 107 | 105 | }); |
| 106 | + }); | |
| 108 | 107 | } |
| @@ -29,80 +29,79 @@ pub fn draw_status_bar(state: &mut AppState, ui: &mut Ui) { | |||
| 29 | ..Default::default() | 29 | ..Default::default() |
| 30 | } | 30 | } |
| 31 | .show(ui, |ui| { | 31 | .show(ui, |ui| { |
| 32 | - ui.spacing_mut().item_spacing.x = 16.0; | 32 | + ui.spacing_mut().item_spacing.x = 16.0; |
| 33 | 33 | ||
| 34 | - // Error message (highest priority) avec icône | 34 | + // Error message (highest priority) avec icône |
| 35 | - if let Some(ref error) = state.error_message { | 35 | + if let Some(ref error) = state.error_message { |
| 36 | - let error_clone = error.clone(); | 36 | + let error_clone = error.clone(); |
| 37 | - ui.horizontal(|ui| { | 37 | + ui.horizontal(|ui| { |
| 38 | - ui.label(egui::RichText::new("❌ ").color(egui::Color32::RED)); | 38 | + ui.label(egui::RichText::new("❌ ").color(egui::Color32::RED)); |
| 39 | - ui.colored_label(egui::Color32::from_rgb(240, 100, 100), &error_clone); | 39 | + ui.colored_label(egui::Color32::from_rgb(240, 100, 100), &error_clone); |
| 40 | - if ui.button(egui::RichText::new("✕").size(14.0)).clicked() { | 40 | + if ui.button(egui::RichText::new("✕").size(14.0)).clicked() { |
| 41 | - state.clear_error(); | 41 | + state.clear_error(); |
| 42 | - } | 42 | + } |
| 43 | - }); | 43 | + }); |
| 44 | - return; | 44 | + return; |
| 45 | - } | 45 | + } |
| 46 | 46 | ||
| 47 | - // Info message avec icône | 47 | + // Info message avec icône |
| 48 | - if let Some(ref info) = state.info_message { | 48 | + if let Some(ref info) = state.info_message { |
| 49 | - let info_clone = info.clone(); | 49 | + let info_clone = info.clone(); |
| 50 | - ui.horizontal(|ui| { | 50 | + ui.horizontal(|ui| { |
| 51 | - ui.label(egui::RichText::new("ℹ️ ").color(egui::Color32::from_rgb(100, 200, 100))); | 51 | + ui.label(egui::RichText::new("ℹ️ ").color(egui::Color32::from_rgb(100, 200, 100))); |
| 52 | - ui.colored_label(egui::Color32::from_rgb(100, 220, 100), &info_clone); | 52 | + ui.colored_label(egui::Color32::from_rgb(100, 220, 100), &info_clone); |
| 53 | - if ui.button(egui::RichText::new("✕").size(14.0)).clicked() { | 53 | + if ui.button(egui::RichText::new("✕").size(14.0)).clicked() { |
| 54 | - state.clear_info(); | 54 | + state.clear_info(); |
| 55 | - } | 55 | + } |
| 56 | - }); | 56 | + }); |
| 57 | - return; | 57 | + return; |
| 58 | - } | 58 | + } |
| 59 | 59 | ||
| 60 | - // Sync status avec icône | 60 | + // Sync status avec icône |
| 61 | - let feed_count = state.feed_count(); | 61 | + let feed_count = state.feed_count(); |
| 62 | - let enabled_count = state.enabled_feed_count(); | 62 | + let enabled_count = state.enabled_feed_count(); |
| 63 | + | ||
| 64 | + // Process sync messages | ||
| 65 | + state.sync_task_manager.process_messages(); | ||
| 66 | + | ||
| 67 | + ui.horizontal(|ui| { | ||
| 68 | + // Use the sync indicator from sync_status module | ||
| 69 | + crate::ui::sync_status::draw_sync_indicator(state, ui); | ||
| 63 | 70 | ||
| 64 | - // Process sync messages and get status | 71 | + // Show ready status only if no sync activity at all |
| 65 | - let is_syncing = state.sync_task_manager.status.is_syncing(); | 72 | + if !state.sync_task_manager.status.has_sync_activity() { |
| 66 | - let global_sync_in_progress = state.sync_task_manager.status.global_sync_in_progress; | 73 | + ui.label(egui::RichText::new("✅ ").color(egui::Color32::from_rgb(100, 220, 100))); |
| 74 | + ui.colored_label(egui::Color32::from_rgb(150, 200, 150), "Prêt"); | ||
| 75 | + } | ||
| 76 | + | ||
| 77 | + // Theme toggle button | ||
| 78 | + let theme_icon = match state.get_theme() { | ||
| 79 | + Theme::Dark => "🌙", | ||
| 80 | + Theme::Light => "☀️", | ||
| 81 | + }; | ||
| 82 | + let theme_tooltip = match state.get_theme() { | ||
| 83 | + Theme::Dark => "Basculer vers le thème clair", | ||
| 84 | + Theme::Light => "Basculer vers le thème sombre", | ||
| 85 | + }; | ||
| 67 | 86 | ||
| 68 | - ui.horizontal(|ui| { | 87 | + if ui.link(egui::RichText::new(theme_icon).size(16.0)) |
| 69 | - // Use the sync indicator from sync_status module | 88 | + .on_hover_text(theme_tooltip) |
| 70 | - crate::ui::sync_status::draw_sync_indicator(state, ui); | 89 | + .clicked() |
| 71 | - | 90 | + { |
| 72 | - // Show ready status if no sync in progress | 91 | + state.toggle_theme(); |
| 73 | - if !is_syncing && !global_sync_in_progress { | 92 | + } |
| 74 | - ui.label(egui::RichText::new("✅ ").color(egui::Color32::from_rgb(100, 220, 100))); | ||
| 75 | - ui.colored_label(egui::Color32::from_rgb(150, 200, 150), "Prêt"); | ||
| 76 | - } | ||
| 77 | 93 | ||
| 78 | - // Theme toggle button | 94 | + // Feed count on the right avec icône |
| 79 | - let theme_icon = match state.get_theme() { | 95 | + ui.with_layout(Layout::right_to_left(Align::Center), |ui| { |
| 80 | - Theme::Dark => "🌙", | 96 | + ui.label(egui::RichText::new(format!("📊 {} flux ", feed_count)) |
| 81 | - Theme::Light => "☀️", | 97 | + .color(egui::Color32::from_rgb(160, 180, 200))); |
| 82 | - }; | ||
| 83 | - let theme_tooltip = match state.get_theme() { | ||
| 84 | - Theme::Dark => "Basculer vers le thème clair", | ||
| 85 | - Theme::Light => "Basculer vers le thème sombre", | ||
| 86 | - }; | ||
| 87 | 98 | ||
| 88 | - if ui.link(egui::RichText::new(theme_icon).size(16.0)) | 99 | + // Nombre de flux activés |
| 89 | - .on_hover_text(theme_tooltip) | 100 | + if enabled_count < feed_count { |
| 90 | - .clicked() | 101 | + ui.label(egui::RichText::new(format!("| {} activés", enabled_count)) |
| 91 | - { | 102 | + .color(egui::Color32::from_rgb(140, 160, 180))); |
| 92 | - state.toggle_theme(); | ||
| 93 | } | 103 | } |
| 94 | - | ||
| 95 | - // Feed count on the right avec icône | ||
| 96 | - ui.with_layout(Layout::right_to_left(Align::Center), |ui| { | ||
| 97 | - ui.label(egui::RichText::new(format!("📊 {} flux ", feed_count)) | ||
| 98 | - .color(egui::Color32::from_rgb(160, 180, 200))); | ||
| 99 | - | ||
| 100 | - // Nombre de flux activés | ||
| 101 | - if enabled_count < feed_count { | ||
| 102 | - ui.label(egui::RichText::new(format!("| {} activés", enabled_count)) | ||
| 103 | - .color(egui::Color32::from_rgb(140, 160, 180))); | ||
| 104 | - } | ||
| 105 | - }); | ||
| 106 | }); | 104 | }); |
| 107 | }); | 105 | }); |
| 106 | + }); | ||
| 108 | } | 107 | } |
modified
src/ui/sync_status.rs +42 -147 | @@ -1,73 +1,9 @@ | ||
| 1 | -//! Sync Status Panel UI component | |
| 1 | +//! Sync Status UI component | |
| 2 | 2 | //! |
| 3 | -//! This module displays the synchronization status and progress. | |
| 3 | +//! This module displays the synchronization status and progress in the status bar. | |
| 4 | 4 | |
| 5 | 5 | use crate::config::AppState; |
| 6 | -use crate::config::sync_task::FeedSyncStatus; | |
| 7 | -use egui::{Color32, Frame, RichText, Ui}; | |
| 8 | - | |
| 9 | -/// Draws the sync status panel | |
| 10 | -pub fn draw_sync_status_panel(state: &mut AppState, ui: &mut Ui) { | |
| 11 | - let status = &state.sync_task_manager.status; | |
| 12 | - | |
| 13 | - // Only show panel if there's sync activity | |
| 14 | - if !status.is_syncing() && !status.global_sync_in_progress { | |
| 15 | - return; | |
| 16 | - } | |
| 17 | - | |
| 18 | - // Create a frame for the sync status panel | |
| 19 | - Frame { | |
| 20 | - fill: Color32::from_rgb(40, 42, 54), | |
| 21 | - stroke: egui::Stroke::new(1.0, Color32::from_rgb(130, 170, 250)), | |
| 22 | - corner_radius: 8.0.into(), | |
| 23 | - inner_margin: egui::Margin::symmetric(12, 8), | |
| 24 | - ..Default::default() | |
| 25 | - } | |
| 26 | - .show(ui, |ui| { | |
| 27 | - ui.vertical(|ui| { | |
| 28 | - // Header | |
| 29 | - ui.horizontal(|ui| { | |
| 30 | - if status.global_sync_in_progress { | |
| 31 | - ui.label(RichText::new("🔄 Synchronisation en cours").size(16.0).strong()); | |
| 32 | - } else { | |
| 33 | - ui.label(RichText::new("📊 Statut de synchronisation").size(16.0).strong()); | |
| 34 | - } | |
| 35 | - | |
| 36 | - // Overall progress bar for global sync | |
| 37 | - if status.global_sync_in_progress { | |
| 38 | - ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { | |
| 39 | - draw_progress_bar(ui, status.get_overall_progress(), 120.0); | |
| 40 | - ui.label(RichText::new(format!("{:.0}%", status.get_overall_progress() * 100.0)) | |
| 41 | - .size(14.0) | |
| 42 | - .color(Color32::from_rgb(220, 230, 245))); | |
| 43 | - }); | |
| 44 | - } | |
| 45 | - }); | |
| 46 | - | |
| 47 | - ui.separator(); | |
| 48 | - ui.add_space(8.0); | |
| 49 | - | |
| 50 | - // Global progress info | |
| 51 | - if status.global_sync_in_progress { | |
| 52 | - ui.label(RichText::new(format!( | |
| 53 | - "Synchronisation de {} flux: {}/{} terminés", | |
| 54 | - status.total_feeds, | |
| 55 | - status.completed_feeds, | |
| 56 | - status.total_feeds | |
| 57 | - )).size(14.0).color(Color32::from_rgb(200, 210, 220))); | |
| 58 | - ui.add_space(8.0); | |
| 59 | - } | |
| 60 | - | |
| 61 | - // Individual feed status | |
| 62 | - ui.label(RichText::new("Statut par flux:").size(14.0).strong()); | |
| 63 | - ui.add_space(4.0); | |
| 64 | - | |
| 65 | - for (feed_id, feed_status) in &status.feed_sync_status { | |
| 66 | - draw_feed_sync_status(ui, feed_id, feed_status); | |
| 67 | - } | |
| 68 | - }); | |
| 69 | - }); | |
| 70 | -} | |
| 6 | +use egui::{Color32, RichText, Ui}; | |
| 71 | 7 | |
| 72 | 8 | /// Draws a progress bar |
| 73 | 9 | fn draw_progress_bar(ui: &mut Ui, progress: f32, width: f32) { |
| @@ -110,104 +46,63 @@ fn draw_progress_bar(ui: &mut Ui, progress: f32, width: f32) { | ||
| 110 | 46 | ui.advance_cursor_after_rect(bg_rect); |
| 111 | 47 | } |
| 112 | 48 | |
| 113 | -/// Draws the sync status for a single feed | |
| 114 | -fn draw_feed_sync_status(ui: &mut Ui, feed_id: &str, status: &FeedSyncStatus) { | |
| 115 | - ui.horizontal(|ui| { | |
| 116 | - ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 4.0); | |
| 117 | - | |
| 118 | - // Feed ID/name (truncated if too long) | |
| 119 | - let display_id = if feed_id.len() > 20 { | |
| 120 | - format!("{}...", &feed_id[..17]) | |
| 121 | - } else { | |
| 122 | - feed_id.to_string() | |
| 123 | - }; | |
| 124 | - | |
| 125 | - match status { | |
| 126 | - FeedSyncStatus::Idle => { | |
| 127 | - ui.label(RichText::new(format!("⏸️ {}", display_id)) | |
| 128 | - .size(13.0) | |
| 129 | - .color(Color32::from_rgb(150, 160, 180))); | |
| 130 | - } | |
| 131 | - FeedSyncStatus::InProgress { progress, message } => { | |
| 132 | - ui.label(RichText::new(format!("🔄 {}", display_id)) | |
| 133 | - .size(13.0) | |
| 134 | - .color(Color32::from_rgb(180, 200, 255))); | |
| 135 | - | |
| 136 | - // Progress bar | |
| 137 | - draw_progress_bar(ui, *progress, 80.0); | |
| 138 | - ui.add_space(4.0); | |
| 139 | - | |
| 140 | - // Status message | |
| 141 | - ui.label(RichText::new(format!("{}", message)) | |
| 142 | - .size(12.0) | |
| 143 | - .color(Color32::from_rgb(180, 190, 210))); | |
| 144 | - } | |
| 145 | - FeedSyncStatus::Completed { episodes_found, episodes_downloaded } => { | |
| 146 | - ui.label(RichText::new(format!("✅ {}", display_id)) | |
| 147 | - .size(13.0) | |
| 148 | - .color(Color32::from_rgb(80, 220, 100))); | |
| 149 | - | |
| 150 | - ui.label(RichText::new(format!("{} épisodes, {} téléchargés", episodes_found, episodes_downloaded)) | |
| 151 | - .size(12.0) | |
| 152 | - .color(Color32::from_rgb(160, 180, 200))); | |
| 153 | - } | |
| 154 | - FeedSyncStatus::Failed { error } => { | |
| 155 | - ui.label(RichText::new(format!("❌ {}", display_id)) | |
| 156 | - .size(13.0) | |
| 157 | - .color(Color32::from_rgb(220, 80, 80))); | |
| 158 | - | |
| 159 | - ui.label(RichText::new(format!("Erreur: {}", error)) | |
| 160 | - .size(12.0) | |
| 161 | - .color(Color32::from_rgb(200, 100, 100))); | |
| 162 | - } | |
| 163 | - } | |
| 164 | - }); | |
| 165 | - | |
| 166 | - ui.add_space(4.0); | |
| 167 | -} | |
| 168 | - | |
| 169 | 49 | /// Draws a compact sync indicator for the status bar |
| 170 | -pub fn draw_sync_indicator(state: &AppState, ui: &mut Ui) { | |
| 50 | +pub fn draw_sync_indicator(state: &mut AppState, ui: &mut Ui) { | |
| 51 | + // Process messages first | |
| 52 | + state.sync_task_manager.process_messages(); | |
| 53 | + | |
| 171 | 54 | let status = &state.sync_task_manager.status; |
| 172 | 55 | |
| 173 | - if !status.is_syncing() { | |
| 56 | + // Always show sync info if there's any activity | |
| 57 | + if !status.has_sync_activity() { | |
| 174 | 58 | return; |
| 175 | 59 | } |
| 176 | 60 | |
| 61 | + let global_sync_in_progress = status.global_sync_in_progress; | |
| 62 | + let syncing_count = status.syncing_feed_count(); | |
| 63 | + let completed_count = status.feed_sync_status.values() | |
| 64 | + .filter(|s| matches!(s, crate::config::sync_task::FeedSyncStatus::Completed { .. })) | |
| 65 | + .count(); | |
| 66 | + let failed_count = status.feed_sync_status.values() | |
| 67 | + .filter(|s| matches!(s, crate::config::sync_task::FeedSyncStatus::Failed { .. })) | |
| 68 | + .count(); | |
| 69 | + let total_feeds = status.total_feeds; | |
| 70 | + let completed_feeds = status.completed_feeds; | |
| 71 | + let overall_progress = status.get_overall_progress(); | |
| 72 | + | |
| 177 | 73 | ui.horizontal(|ui| { |
| 178 | 74 | ui.spacing_mut().item_spacing = egui::Vec2::new(4.0, 0.0); |
| 179 | 75 | |
| 180 | - if status.global_sync_in_progress { | |
| 76 | + if global_sync_in_progress { | |
| 181 | 77 | // Global sync indicator |
| 182 | 78 | ui.label(RichText::new("🔄").size(16.0)); |
| 183 | 79 | ui.label(RichText::new(format!("Synchro: {}/{} flux", |
| 184 | - status.completed_feeds, | |
| 185 | - status.total_feeds)) | |
| 80 | + completed_feeds, | |
| 81 | + total_feeds)) | |
| 186 | 82 | .size(13.0) |
| 187 | 83 | .color(Color32::from_rgb(180, 200, 255))); |
| 188 | 84 | |
| 189 | 85 | // Overall progress bar |
| 190 | - draw_progress_bar(ui, status.get_overall_progress(), 60.0); | |
| 191 | - } else { | |
| 86 | + draw_progress_bar(ui, overall_progress, 60.0); | |
| 87 | + } else if syncing_count > 0 { | |
| 192 | 88 | // Individual feed sync indicator |
| 193 | - let syncing_count = status.syncing_feed_count(); | |
| 194 | - if syncing_count > 0 { | |
| 195 | - ui.label(RichText::new("🔄").size(16.0)); | |
| 196 | - ui.label(RichText::new(format!("{} flux en synchro", syncing_count)) | |
| 197 | - .size(13.0) | |
| 198 | - .color(Color32::from_rgb(180, 200, 255))); | |
| 89 | + ui.label(RichText::new("🔄").size(16.0)); | |
| 90 | + ui.label(RichText::new(format!("{} flux en synchro", syncing_count)) | |
| 91 | + .size(13.0) | |
| 92 | + .color(Color32::from_rgb(180, 200, 255))); | |
| 93 | + } else if completed_count > 0 || failed_count > 0 { | |
| 94 | + // Show completion status briefly | |
| 95 | + ui.label(RichText::new("✅").size(16.0).color(Color32::from_rgb(80, 220, 100))); | |
| 96 | + let total_completed = completed_count + failed_count; | |
| 97 | + ui.label(RichText::new(format!("Synchro terminée: {} flux ({} OK, {} erreur)", | |
| 98 | + total_completed, completed_count, failed_count)) | |
| 99 | + .size(13.0) | |
| 100 | + .color(Color32::from_rgb(150, 220, 150))); | |
| 101 | + | |
| 102 | + // Add a button to clear completed sync status | |
| 103 | + if ui.button(RichText::new("✕").size(12.0)).clicked() { | |
| 104 | + state.sync_task_manager.clear_completed_sync_status(); | |
| 199 | 105 | } |
| 200 | 106 | } |
| 201 | 107 | }); |
| 202 | 108 | } |
| 203 | - | |
| 204 | -#[cfg(test)] | |
| 205 | -mod tests { | |
| 206 | - use super::*; | |
| 207 | - | |
| 208 | - #[test] | |
| 209 | - fn test_draw_progress_bar() { | |
| 210 | - // This is a UI test, can't easily test rendering | |
| 211 | - // Just verify the function doesn't panic | |
| 212 | - } | |
| 213 | -} | |
| @@ -1,73 +1,9 @@ | |||
| 1 | -//! Sync Status Panel UI component | 1 | +//! Sync Status UI component |
| 2 | //! | 2 | //! |
| 3 | -//! This module displays the synchronization status and progress. | 3 | +//! This module displays the synchronization status and progress in the status bar. |
| 4 | 4 | ||
| 5 | use crate::config::AppState; | 5 | use crate::config::AppState; |
| 6 | -use crate::config::sync_task::FeedSyncStatus; | 6 | +use egui::{Color32, RichText, Ui}; |
| 7 | -use egui::{Color32, Frame, RichText, Ui}; | ||
| 8 | - | ||
| 9 | -/// Draws the sync status panel | ||
| 10 | -pub fn draw_sync_status_panel(state: &mut AppState, ui: &mut Ui) { | ||
| 11 | - let status = &state.sync_task_manager.status; | ||
| 12 | - | ||
| 13 | - // Only show panel if there's sync activity | ||
| 14 | - if !status.is_syncing() && !status.global_sync_in_progress { | ||
| 15 | - return; | ||
| 16 | - } | ||
| 17 | - | ||
| 18 | - // Create a frame for the sync status panel | ||
| 19 | - Frame { | ||
| 20 | - fill: Color32::from_rgb(40, 42, 54), | ||
| 21 | - stroke: egui::Stroke::new(1.0, Color32::from_rgb(130, 170, 250)), | ||
| 22 | - corner_radius: 8.0.into(), | ||
| 23 | - inner_margin: egui::Margin::symmetric(12, 8), | ||
| 24 | - ..Default::default() | ||
| 25 | - } | ||
| 26 | - .show(ui, |ui| { | ||
| 27 | - ui.vertical(|ui| { | ||
| 28 | - // Header | ||
| 29 | - ui.horizontal(|ui| { | ||
| 30 | - if status.global_sync_in_progress { | ||
| 31 | - ui.label(RichText::new("🔄 Synchronisation en cours").size(16.0).strong()); | ||
| 32 | - } else { | ||
| 33 | - ui.label(RichText::new("📊 Statut de synchronisation").size(16.0).strong()); | ||
| 34 | - } | ||
| 35 | - | ||
| 36 | - // Overall progress bar for global sync | ||
| 37 | - if status.global_sync_in_progress { | ||
| 38 | - ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { | ||
| 39 | - draw_progress_bar(ui, status.get_overall_progress(), 120.0); | ||
| 40 | - ui.label(RichText::new(format!("{:.0}%", status.get_overall_progress() * 100.0)) | ||
| 41 | - .size(14.0) | ||
| 42 | - .color(Color32::from_rgb(220, 230, 245))); | ||
| 43 | - }); | ||
| 44 | - } | ||
| 45 | - }); | ||
| 46 | - | ||
| 47 | - ui.separator(); | ||
| 48 | - ui.add_space(8.0); | ||
| 49 | - | ||
| 50 | - // Global progress info | ||
| 51 | - if status.global_sync_in_progress { | ||
| 52 | - ui.label(RichText::new(format!( | ||
| 53 | - "Synchronisation de {} flux: {}/{} terminés", | ||
| 54 | - status.total_feeds, | ||
| 55 | - status.completed_feeds, | ||
| 56 | - status.total_feeds | ||
| 57 | - )).size(14.0).color(Color32::from_rgb(200, 210, 220))); | ||
| 58 | - ui.add_space(8.0); | ||
| 59 | - } | ||
| 60 | - | ||
| 61 | - // Individual feed status | ||
| 62 | - ui.label(RichText::new("Statut par flux:").size(14.0).strong()); | ||
| 63 | - ui.add_space(4.0); | ||
| 64 | - | ||
| 65 | - for (feed_id, feed_status) in &status.feed_sync_status { | ||
| 66 | - draw_feed_sync_status(ui, feed_id, feed_status); | ||
| 67 | - } | ||
| 68 | - }); | ||
| 69 | - }); | ||
| 70 | -} | ||
| 71 | 7 | ||
| 72 | /// Draws a progress bar | 8 | /// Draws a progress bar |
| 73 | fn draw_progress_bar(ui: &mut Ui, progress: f32, width: f32) { | 9 | fn draw_progress_bar(ui: &mut Ui, progress: f32, width: f32) { |
| @@ -110,104 +46,63 @@ fn draw_progress_bar(ui: &mut Ui, progress: f32, width: f32) { | |||
| 110 | ui.advance_cursor_after_rect(bg_rect); | 46 | ui.advance_cursor_after_rect(bg_rect); |
| 111 | } | 47 | } |
| 112 | 48 | ||
| 113 | -/// Draws the sync status for a single feed | ||
| 114 | -fn draw_feed_sync_status(ui: &mut Ui, feed_id: &str, status: &FeedSyncStatus) { | ||
| 115 | - ui.horizontal(|ui| { | ||
| 116 | - ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 4.0); | ||
| 117 | - | ||
| 118 | - // Feed ID/name (truncated if too long) | ||
| 119 | - let display_id = if feed_id.len() > 20 { | ||
| 120 | - format!("{}...", &feed_id[..17]) | ||
| 121 | - } else { | ||
| 122 | - feed_id.to_string() | ||
| 123 | - }; | ||
| 124 | - | ||
| 125 | - match status { | ||
| 126 | - FeedSyncStatus::Idle => { | ||
| 127 | - ui.label(RichText::new(format!("⏸️ {}", display_id)) | ||
| 128 | - .size(13.0) | ||
| 129 | - .color(Color32::from_rgb(150, 160, 180))); | ||
| 130 | - } | ||
| 131 | - FeedSyncStatus::InProgress { progress, message } => { | ||
| 132 | - ui.label(RichText::new(format!("🔄 {}", display_id)) | ||
| 133 | - .size(13.0) | ||
| 134 | - .color(Color32::from_rgb(180, 200, 255))); | ||
| 135 | - | ||
| 136 | - // Progress bar | ||
| 137 | - draw_progress_bar(ui, *progress, 80.0); | ||
| 138 | - ui.add_space(4.0); | ||
| 139 | - | ||
| 140 | - // Status message | ||
| 141 | - ui.label(RichText::new(format!("{}", message)) | ||
| 142 | - .size(12.0) | ||
| 143 | - .color(Color32::from_rgb(180, 190, 210))); | ||
| 144 | - } | ||
| 145 | - FeedSyncStatus::Completed { episodes_found, episodes_downloaded } => { | ||
| 146 | - ui.label(RichText::new(format!("✅ {}", display_id)) | ||
| 147 | - .size(13.0) | ||
| 148 | - .color(Color32::from_rgb(80, 220, 100))); | ||
| 149 | - | ||
| 150 | - ui.label(RichText::new(format!("{} épisodes, {} téléchargés", episodes_found, episodes_downloaded)) | ||
| 151 | - .size(12.0) | ||
| 152 | - .color(Color32::from_rgb(160, 180, 200))); | ||
| 153 | - } | ||
| 154 | - FeedSyncStatus::Failed { error } => { | ||
| 155 | - ui.label(RichText::new(format!("❌ {}", display_id)) | ||
| 156 | - .size(13.0) | ||
| 157 | - .color(Color32::from_rgb(220, 80, 80))); | ||
| 158 | - | ||
| 159 | - ui.label(RichText::new(format!("Erreur: {}", error)) | ||
| 160 | - .size(12.0) | ||
| 161 | - .color(Color32::from_rgb(200, 100, 100))); | ||
| 162 | - } | ||
| 163 | - } | ||
| 164 | - }); | ||
| 165 | - | ||
| 166 | - ui.add_space(4.0); | ||
| 167 | -} | ||
| 168 | - | ||
| 169 | /// Draws a compact sync indicator for the status bar | 49 | /// Draws a compact sync indicator for the status bar |
| 170 | -pub fn draw_sync_indicator(state: &AppState, ui: &mut Ui) { | 50 | +pub fn draw_sync_indicator(state: &mut AppState, ui: &mut Ui) { |
| 51 | + // Process messages first | ||
| 52 | + state.sync_task_manager.process_messages(); | ||
| 53 | + | ||
| 171 | let status = &state.sync_task_manager.status; | 54 | let status = &state.sync_task_manager.status; |
| 172 | 55 | ||
| 173 | - if !status.is_syncing() { | 56 | + // Always show sync info if there's any activity |
| 57 | + if !status.has_sync_activity() { | ||
| 174 | return; | 58 | return; |
| 175 | } | 59 | } |
| 176 | 60 | ||
| 61 | + let global_sync_in_progress = status.global_sync_in_progress; | ||
| 62 | + let syncing_count = status.syncing_feed_count(); | ||
| 63 | + let completed_count = status.feed_sync_status.values() | ||
| 64 | + .filter(|s| matches!(s, crate::config::sync_task::FeedSyncStatus::Completed { .. })) | ||
| 65 | + .count(); | ||
| 66 | + let failed_count = status.feed_sync_status.values() | ||
| 67 | + .filter(|s| matches!(s, crate::config::sync_task::FeedSyncStatus::Failed { .. })) | ||
| 68 | + .count(); | ||
| 69 | + let total_feeds = status.total_feeds; | ||
| 70 | + let completed_feeds = status.completed_feeds; | ||
| 71 | + let overall_progress = status.get_overall_progress(); | ||
| 72 | + | ||
| 177 | ui.horizontal(|ui| { | 73 | ui.horizontal(|ui| { |
| 178 | ui.spacing_mut().item_spacing = egui::Vec2::new(4.0, 0.0); | 74 | ui.spacing_mut().item_spacing = egui::Vec2::new(4.0, 0.0); |
| 179 | 75 | ||
| 180 | - if status.global_sync_in_progress { | 76 | + if global_sync_in_progress { |
| 181 | // Global sync indicator | 77 | // Global sync indicator |
| 182 | ui.label(RichText::new("🔄").size(16.0)); | 78 | ui.label(RichText::new("🔄").size(16.0)); |
| 183 | ui.label(RichText::new(format!("Synchro: {}/{} flux", | 79 | ui.label(RichText::new(format!("Synchro: {}/{} flux", |
| 184 | - status.completed_feeds, | 80 | + completed_feeds, |
| 185 | - status.total_feeds)) | 81 | + total_feeds)) |
| 186 | .size(13.0) | 82 | .size(13.0) |
| 187 | .color(Color32::from_rgb(180, 200, 255))); | 83 | .color(Color32::from_rgb(180, 200, 255))); |
| 188 | 84 | ||
| 189 | // Overall progress bar | 85 | // Overall progress bar |
| 190 | - draw_progress_bar(ui, status.get_overall_progress(), 60.0); | 86 | + draw_progress_bar(ui, overall_progress, 60.0); |
| 191 | - } else { | 87 | + } else if syncing_count > 0 { |
| 192 | // Individual feed sync indicator | 88 | // Individual feed sync indicator |
| 193 | - let syncing_count = status.syncing_feed_count(); | 89 | + ui.label(RichText::new("🔄").size(16.0)); |
| 194 | - if syncing_count > 0 { | 90 | + ui.label(RichText::new(format!("{} flux en synchro", syncing_count)) |
| 195 | - ui.label(RichText::new("🔄").size(16.0)); | 91 | + .size(13.0) |
| 196 | - ui.label(RichText::new(format!("{} flux en synchro", syncing_count)) | 92 | + .color(Color32::from_rgb(180, 200, 255))); |
| 197 | - .size(13.0) | 93 | + } else if completed_count > 0 || failed_count > 0 { |
| 198 | - .color(Color32::from_rgb(180, 200, 255))); | 94 | + // Show completion status briefly |
| 95 | + ui.label(RichText::new("✅").size(16.0).color(Color32::from_rgb(80, 220, 100))); | ||
| 96 | + let total_completed = completed_count + failed_count; | ||
| 97 | + ui.label(RichText::new(format!("Synchro terminée: {} flux ({} OK, {} erreur)", | ||
| 98 | + total_completed, completed_count, failed_count)) | ||
| 99 | + .size(13.0) | ||
| 100 | + .color(Color32::from_rgb(150, 220, 150))); | ||
| 101 | + | ||
| 102 | + // Add a button to clear completed sync status | ||
| 103 | + if ui.button(RichText::new("✕").size(12.0)).clicked() { | ||
| 104 | + state.sync_task_manager.clear_completed_sync_status(); | ||
| 199 | } | 105 | } |
| 200 | } | 106 | } |
| 201 | }); | 107 | }); |
| 202 | } | 108 | } |
| 203 | - | ||
| 204 | -#[cfg(test)] | ||
| 205 | -mod tests { | ||
| 206 | - use super::*; | ||
| 207 | - | ||
| 208 | - #[test] | ||
| 209 | - fn test_draw_progress_bar() { | ||
| 210 | - // This is a UI test, can't easily test rendering | ||
| 211 | - // Just verify the function doesn't panic | ||
| 212 | - } | ||
| 213 | -} | ||