diff --git a/src/gui/message.rs b/src/gui/message.rs index 12fb76a..5c2edc1 100644 --- a/src/gui/message.rs +++ b/src/gui/message.rs @@ -13,8 +13,9 @@ use tracing::*; use super::SelfUpdateProgress; use super::{ request_counter::{RequestCounter, RequestID}, - App, LastActionStatus, SpecFetchProgress, WindowProviderParameters, + App, SpecFetchProgress, WindowProviderParameters, }; +use crate::gui::LastAction; use crate::integrate::*; use crate::mod_lints::{LintId, LintReport}; use crate::state::{ModData_v0_1_0 as ModData, ModOrGroup}; @@ -92,7 +93,7 @@ impl ResolveMods { .unwrap(); ctx.request_repaint(); }); - app.last_action_status = LastActionStatus::Idle; + app.last_action = None; app.resolve_mod_rid = Some(MessageHandle { rid, handle, @@ -155,18 +156,19 @@ impl ResolveMods { app.resolve_mod.clear(); app.sort_mods(); app.state.mod_data.save().unwrap(); - app.last_action_status = - LastActionStatus::Success("mods successfully resolved".to_string()); + app.last_action = Some(LastAction::success( + "mods successfully resolved".to_string(), + )); } Err(ProviderError::NoProvider { url: _, factory }) => { app.window_provider_parameters = Some(WindowProviderParameters::new(factory, &app.state)); - app.last_action_status = LastActionStatus::Failure("no provider".to_string()); + app.last_action = Some(LastAction::failure("no provider".to_string())); } Err(e) => { error!("{}", e); app.problematic_mod_id = e.opt_mod_id(); - app.last_action_status = LastActionStatus::Failure(e.to_string()); + app.last_action = Some(LastAction::failure(e.to_string())); } } app.resolve_mod_rid = None; @@ -211,8 +213,7 @@ impl Integrate { match self.result { Ok(()) => { info!("integration complete"); - app.last_action_status = - LastActionStatus::Success("integration complete".to_string()); + app.last_action = Some(LastAction::success("integration complete".to_string())); } Err(ref e) if let IntegrationError::ProviderError { ref source } = e @@ -220,12 +221,12 @@ impl Integrate { { app.window_provider_parameters = Some(WindowProviderParameters::new(factory, &app.state)); - app.last_action_status = LastActionStatus::Failure("no provider".to_string()); + app.last_action = Some(LastAction::failure("no provider".to_string())); } Err(e) => { error!("{}", e); app.problematic_mod_id = e.opt_mod_id(); - app.last_action_status = LastActionStatus::Failure(e.to_string()); + app.last_action = Some(LastAction::failure(e.to_string())); } } app.integrate_rid = None; @@ -267,7 +268,7 @@ impl UpdateCache { .await .unwrap(); }); - app.last_action_status = LastActionStatus::Idle; + app.last_action = None; app.update_rid = Some(MessageHandle { rid, handle, @@ -280,18 +281,19 @@ impl UpdateCache { match self.result { Ok(()) => { info!("cache update complete"); - app.last_action_status = - LastActionStatus::Success("successfully updated cache".to_string()); + app.last_action = Some(LastAction::success( + "successfully updated cache".to_string(), + )); } Err(ProviderError::NoProvider { url: _, factory }) => { app.window_provider_parameters = Some(WindowProviderParameters::new(factory, &app.state)); - app.last_action_status = LastActionStatus::Failure("no provider".to_string()); + app.last_action = Some(LastAction::failure("no provider".to_string())); } Err(e) => { error!("{}", e); app.problematic_mod_id = e.opt_mod_id(); - app.last_action_status = LastActionStatus::Failure(e.to_string()); + app.last_action = Some(LastAction::failure(e.to_string())); } } app.update_rid = None; @@ -469,8 +471,8 @@ impl LintMods { Ok(report) => { info!("lint mod report complete"); app.lint_report = Some(report); - app.last_action_status = - LastActionStatus::Success("lint mod report complete".to_string()); + app.last_action = + Some(LastAction::success("lint mod report complete".to_string())); } Err(ref e) if let IntegrationError::ProviderError { ref source } = e @@ -478,12 +480,12 @@ impl LintMods { { app.window_provider_parameters = Some(WindowProviderParameters::new(factory, &app.state)); - app.last_action_status = LastActionStatus::Failure("no provider".to_string()); + app.last_action = Some(LastAction::failure("no provider".to_string())); } Err(e) => { error!("{}", e); app.problematic_mod_id = e.opt_mod_id(); - app.last_action_status = LastActionStatus::Failure(e.to_string()); + app.last_action = Some(LastAction::failure(e.to_string())); } } app.integrate_rid = None; @@ -568,15 +570,13 @@ impl SelfUpdate { Ok(original_exe_path) => { info!("self update complete"); app.original_exe_path = Some(original_exe_path); - app.last_action_status = - LastActionStatus::Success("self update complete".to_string()); + app.last_action = Some(LastAction::success("self update complete".to_string())); } Err(e) => { error!("self update failed"); error!("{:#?}", e); app.self_update_rid = None; - app.last_action_status = - LastActionStatus::Failure("self update failed".to_string()); + app.last_action = Some(LastAction::failure("self update failed".to_string())); } } app.integrate_rid = None; diff --git a/src/gui/mod.rs b/src/gui/mod.rs index 0126537..21df8ec 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -9,7 +9,7 @@ mod toggle_switch; use std::cmp::Ordering; use std::collections::{BTreeMap, BTreeSet}; use std::ops::{Deref, RangeInclusive}; -use std::time::{Duration, SystemTime}; +use std::time::{Duration, Instant, SystemTime}; use std::{ collections::{HashMap, HashSet}, ops::DerefMut, @@ -138,7 +138,7 @@ pub struct App { focus_search: bool, settings_window: Option, modio_texture_handle: Option, - last_action_status: LastActionStatus, + last_action: Option, available_update: Option, show_update_time: Option, open_profiles: HashSet, @@ -168,8 +168,37 @@ struct LintOptions { unmodified_game_assets: bool, } +struct LastAction { + timestamp: Instant, + status: LastActionStatus, +} +impl LastAction { + fn success(msg: String) -> Self { + Self { + timestamp: Instant::now(), + status: LastActionStatus::Success(msg), + } + } + fn failure(msg: String) -> Self { + Self { + timestamp: Instant::now(), + status: LastActionStatus::Failure(msg), + } + } + fn timeago(&self) -> String { + let duration = Instant::now().duration_since(self.timestamp); + let seconds = duration.as_secs(); + if seconds < 60 { + format!("{}s ago", seconds) + } else if seconds < 3600 { + format!("{}m ago", seconds / 60) + } else { + ">1h ago".into() + } + } +} + enum LastActionStatus { - Idle, Success(String), Failure(String), } @@ -206,7 +235,7 @@ impl App { focus_search: false, settings_window: None, modio_texture_handle: None, - last_action_status: LastActionStatus::Idle, + last_action: None, available_update: None, show_update_time: None, open_profiles: Default::default(), @@ -1745,7 +1774,7 @@ impl eframe::App for App { mods.push(config.spec.clone()); } - self.last_action_status = LastActionStatus::Idle; + self.last_action = None; self.integrate_rid = Some(message::Integrate::send( &mut self.request_counter, self.state.store.clone(), @@ -1767,7 +1796,7 @@ impl eframe::App for App { ); } if button.clicked() { - self.last_action_status = LastActionStatus::Idle; + self.last_action = None; if let Some(pak_path) = &self.state.config.drg_pak_path { let mut mods = HashSet::default(); let active_profile = self.state.mod_data.active_profile.clone(); @@ -1786,18 +1815,14 @@ impl eframe::App for App { ); debug!("uninstalling mods: pak_path = {}", pak_path.display()); - match uninstall(pak_path, mods) { - Ok(()) => { - self.last_action_status = LastActionStatus::Success( - "Successfully uninstalled mods".to_string(), - ); - } - Err(e) => { - self.last_action_status = LastActionStatus::Failure( - format!("Failed to uninstall mods: {e}"), - ) - } - } + self.last_action = Some(match uninstall(pak_path, mods) { + Ok(()) => LastAction::success( + "Successfully uninstalled mods".to_string(), + ), + Err(e) => LastAction::failure(format!( + "Failed to uninstall mods: {e}" + )), + }) } } }); @@ -1854,24 +1879,27 @@ impl eframe::App for App { } } ui.with_layout(egui::Layout::left_to_right(Align::TOP), |ui| { - match &self.last_action_status { - LastActionStatus::Success(msg) => { - ui.label( - egui::RichText::new("STATUS") - .color(Color32::BLACK) - .background_color(Color32::LIGHT_GREEN), - ); - ui.label(msg); - } - LastActionStatus::Failure(msg) => { - ui.label( - egui::RichText::new("STATUS") - .color(Color32::BLACK) - .background_color(Color32::LIGHT_RED), - ); - ui.label(msg); - } - _ => {} + if let Some(last_action) = &self.last_action { + let msg = match &last_action.status { + LastActionStatus::Success(msg) => { + ui.label( + egui::RichText::new("STATUS") + .color(Color32::BLACK) + .background_color(Color32::LIGHT_GREEN), + ); + msg + } + LastActionStatus::Failure(msg) => { + ui.label( + egui::RichText::new("STATUS") + .color(Color32::BLACK) + .background_color(Color32::LIGHT_RED), + ); + msg + } + }; + ui.ctx().request_repaint(); // for continuously updating time + ui.label(format!("({}): {}", last_action.timeago(), msg)); } }); });