diff --git a/src/app.rs b/src/app.rs index 4c0f801f5..2116b1723 100644 --- a/src/app.rs +++ b/src/app.rs @@ -9,6 +9,7 @@ use crate::components::core_zmq_listener::{CoreZMQListener, ZMQMessage}; use crate::context::AppContext; use crate::database::Database; use crate::logging::initialize_logger; +use crate::model::settings::Settings; use crate::ui::contracts_documents::contracts_documents_screen::DocumentQueryScreen; use crate::ui::dpns::dpns_contested_names_screen::{ DPNSScreen, DPNSSubscreen, ScheduledVoteCastingStatus, @@ -33,7 +34,6 @@ use derive_more::From; use eframe::{App, egui}; use std::collections::BTreeMap; use std::ops::BitOrAssign; -use std::path::PathBuf; use std::sync::{Arc, mpsc}; use std::time::{Duration, Instant, SystemTime}; use std::vec; @@ -155,14 +155,14 @@ impl AppState { let db = Arc::new(Database::new(&db_file_path).unwrap()); db.initialize(&db_file_path).unwrap(); - let settings = db.get_settings().expect("expected to get settings"); - - let (password_info, theme_preference) = - if let Some((_, _, password_info, _, _, theme_pref)) = settings.clone() { - (password_info, theme_pref) - } else { - (None, ThemeMode::System) // Default values if no settings found - }; + let settings = db + .get_settings() + .expect("expected to get settings") + .map(Settings::from) + .unwrap_or_default(); + let password_info = settings.password_info; + let theme_preference = settings.theme_mode; + let overwrite_dash_conf = settings.overwrite_dash_conf; let subtasks = Arc::new(TaskManager::new()); let mainnet_app_context = match AppContext::new( @@ -226,113 +226,89 @@ impl AppState { let mut token_creator_screen = TokensScreen::new(&mainnet_app_context, TokensSubscreen::TokenCreator); - let (custom_dash_qt_path, overwrite_dash_conf) = match settings.clone() { - Some((.., custom_dash_qt_path, db_overwrite_dash_conf, _theme_pref)) => { - // Use the stored settings - let custom_dash_qt_path = custom_dash_qt_path.or_else(detect_dash_qt_path); - (custom_dash_qt_path, db_overwrite_dash_conf) - } - None => { - // Only use defaults if there are no settings at all - (None, true) - } - }; - let mut network_chooser_screen = NetworkChooserScreen::new( &mainnet_app_context, testnet_app_context.as_ref(), devnet_app_context.as_ref(), local_app_context.as_ref(), Network::Dash, - custom_dash_qt_path, overwrite_dash_conf, ); let mut wallets_balances_screen = WalletsBalancesScreen::new(&mainnet_app_context); - let mut selected_main_screen = RootScreenType::RootScreenIdentities; - - let mut chosen_network = Network::Dash; - - if let Some((network, screen_type, _password_info, _, _, _)) = settings { - selected_main_screen = screen_type; - chosen_network = network; - network_chooser_screen.current_network = chosen_network; - - if chosen_network == Network::Testnet && testnet_app_context.is_some() { - let testnet_app_context = testnet_app_context.as_ref().unwrap(); - identities_screen = IdentitiesScreen::new(testnet_app_context); - dpns_active_contests_screen = - DPNSScreen::new(testnet_app_context, DPNSSubscreen::Active); - dpns_past_contests_screen = - DPNSScreen::new(testnet_app_context, DPNSSubscreen::Past); - dpns_my_usernames_screen = - DPNSScreen::new(testnet_app_context, DPNSSubscreen::Owned); - dpns_scheduled_votes_screen = - DPNSScreen::new(testnet_app_context, DPNSSubscreen::ScheduledVotes); - transition_visualizer_screen = TransitionVisualizerScreen::new(testnet_app_context); - proof_visualizer_screen = ProofVisualizerScreen::new(testnet_app_context); - document_visualizer_screen = DocumentVisualizerScreen::new(testnet_app_context); - contract_visualizer_screen = ContractVisualizerScreen::new(testnet_app_context); - document_query_screen = DocumentQueryScreen::new(testnet_app_context); - wallets_balances_screen = WalletsBalancesScreen::new(testnet_app_context); - proof_log_screen = ProofLogScreen::new(testnet_app_context); - platform_info_screen = PlatformInfoScreen::new(testnet_app_context); - tokens_balances_screen = - TokensScreen::new(testnet_app_context, TokensSubscreen::MyTokens); - token_search_screen = - TokensScreen::new(testnet_app_context, TokensSubscreen::SearchTokens); - token_creator_screen = - TokensScreen::new(testnet_app_context, TokensSubscreen::TokenCreator); - } else if chosen_network == Network::Devnet && devnet_app_context.is_some() { - let devnet_app_context = devnet_app_context.as_ref().unwrap(); - identities_screen = IdentitiesScreen::new(devnet_app_context); - dpns_active_contests_screen = - DPNSScreen::new(devnet_app_context, DPNSSubscreen::Active); - dpns_past_contests_screen = - DPNSScreen::new(devnet_app_context, DPNSSubscreen::Past); - dpns_my_usernames_screen = - DPNSScreen::new(devnet_app_context, DPNSSubscreen::Owned); - dpns_scheduled_votes_screen = - DPNSScreen::new(devnet_app_context, DPNSSubscreen::ScheduledVotes); - transition_visualizer_screen = TransitionVisualizerScreen::new(devnet_app_context); - proof_visualizer_screen = ProofVisualizerScreen::new(devnet_app_context); - document_visualizer_screen = DocumentVisualizerScreen::new(devnet_app_context); - document_query_screen = DocumentQueryScreen::new(devnet_app_context); - contract_visualizer_screen = ContractVisualizerScreen::new(devnet_app_context); - wallets_balances_screen = WalletsBalancesScreen::new(devnet_app_context); - proof_log_screen = ProofLogScreen::new(devnet_app_context); - platform_info_screen = PlatformInfoScreen::new(devnet_app_context); - tokens_balances_screen = - TokensScreen::new(devnet_app_context, TokensSubscreen::MyTokens); - token_search_screen = - TokensScreen::new(devnet_app_context, TokensSubscreen::SearchTokens); - token_creator_screen = - TokensScreen::new(devnet_app_context, TokensSubscreen::TokenCreator); - } else if chosen_network == Network::Regtest && local_app_context.is_some() { - let local_app_context = local_app_context.as_ref().unwrap(); - identities_screen = IdentitiesScreen::new(local_app_context); - dpns_active_contests_screen = - DPNSScreen::new(local_app_context, DPNSSubscreen::Active); - dpns_past_contests_screen = DPNSScreen::new(local_app_context, DPNSSubscreen::Past); - dpns_my_usernames_screen = DPNSScreen::new(local_app_context, DPNSSubscreen::Owned); - dpns_scheduled_votes_screen = - DPNSScreen::new(local_app_context, DPNSSubscreen::ScheduledVotes); - transition_visualizer_screen = TransitionVisualizerScreen::new(local_app_context); - proof_visualizer_screen = ProofVisualizerScreen::new(local_app_context); - document_visualizer_screen = DocumentVisualizerScreen::new(local_app_context); - contract_visualizer_screen = ContractVisualizerScreen::new(local_app_context); - document_query_screen = DocumentQueryScreen::new(local_app_context); - wallets_balances_screen = WalletsBalancesScreen::new(local_app_context); - proof_log_screen = ProofLogScreen::new(local_app_context); - platform_info_screen = PlatformInfoScreen::new(local_app_context); - tokens_balances_screen = - TokensScreen::new(local_app_context, TokensSubscreen::MyTokens); - token_search_screen = - TokensScreen::new(local_app_context, TokensSubscreen::SearchTokens); - token_creator_screen = - TokensScreen::new(local_app_context, TokensSubscreen::TokenCreator); - } + let selected_main_screen = settings.root_screen_type; + let chosen_network = settings.network; + network_chooser_screen.current_network = chosen_network; + + if chosen_network == Network::Testnet && testnet_app_context.is_some() { + let testnet_app_context = testnet_app_context.as_ref().unwrap(); + identities_screen = IdentitiesScreen::new(testnet_app_context); + dpns_active_contests_screen = + DPNSScreen::new(testnet_app_context, DPNSSubscreen::Active); + dpns_past_contests_screen = DPNSScreen::new(testnet_app_context, DPNSSubscreen::Past); + dpns_my_usernames_screen = DPNSScreen::new(testnet_app_context, DPNSSubscreen::Owned); + dpns_scheduled_votes_screen = + DPNSScreen::new(testnet_app_context, DPNSSubscreen::ScheduledVotes); + transition_visualizer_screen = TransitionVisualizerScreen::new(testnet_app_context); + proof_visualizer_screen = ProofVisualizerScreen::new(testnet_app_context); + document_visualizer_screen = DocumentVisualizerScreen::new(testnet_app_context); + contract_visualizer_screen = ContractVisualizerScreen::new(testnet_app_context); + document_query_screen = DocumentQueryScreen::new(testnet_app_context); + wallets_balances_screen = WalletsBalancesScreen::new(testnet_app_context); + proof_log_screen = ProofLogScreen::new(testnet_app_context); + platform_info_screen = PlatformInfoScreen::new(testnet_app_context); + tokens_balances_screen = + TokensScreen::new(testnet_app_context, TokensSubscreen::MyTokens); + token_search_screen = + TokensScreen::new(testnet_app_context, TokensSubscreen::SearchTokens); + token_creator_screen = + TokensScreen::new(testnet_app_context, TokensSubscreen::TokenCreator); + } else if chosen_network == Network::Devnet && devnet_app_context.is_some() { + let devnet_app_context = devnet_app_context.as_ref().unwrap(); + identities_screen = IdentitiesScreen::new(devnet_app_context); + dpns_active_contests_screen = + DPNSScreen::new(devnet_app_context, DPNSSubscreen::Active); + dpns_past_contests_screen = DPNSScreen::new(devnet_app_context, DPNSSubscreen::Past); + dpns_my_usernames_screen = DPNSScreen::new(devnet_app_context, DPNSSubscreen::Owned); + dpns_scheduled_votes_screen = + DPNSScreen::new(devnet_app_context, DPNSSubscreen::ScheduledVotes); + transition_visualizer_screen = TransitionVisualizerScreen::new(devnet_app_context); + proof_visualizer_screen = ProofVisualizerScreen::new(devnet_app_context); + document_visualizer_screen = DocumentVisualizerScreen::new(devnet_app_context); + document_query_screen = DocumentQueryScreen::new(devnet_app_context); + contract_visualizer_screen = ContractVisualizerScreen::new(devnet_app_context); + wallets_balances_screen = WalletsBalancesScreen::new(devnet_app_context); + proof_log_screen = ProofLogScreen::new(devnet_app_context); + platform_info_screen = PlatformInfoScreen::new(devnet_app_context); + tokens_balances_screen = + TokensScreen::new(devnet_app_context, TokensSubscreen::MyTokens); + token_search_screen = + TokensScreen::new(devnet_app_context, TokensSubscreen::SearchTokens); + token_creator_screen = + TokensScreen::new(devnet_app_context, TokensSubscreen::TokenCreator); + } else if chosen_network == Network::Regtest && local_app_context.is_some() { + let local_app_context = local_app_context.as_ref().unwrap(); + identities_screen = IdentitiesScreen::new(local_app_context); + dpns_active_contests_screen = DPNSScreen::new(local_app_context, DPNSSubscreen::Active); + dpns_past_contests_screen = DPNSScreen::new(local_app_context, DPNSSubscreen::Past); + dpns_my_usernames_screen = DPNSScreen::new(local_app_context, DPNSSubscreen::Owned); + dpns_scheduled_votes_screen = + DPNSScreen::new(local_app_context, DPNSSubscreen::ScheduledVotes); + transition_visualizer_screen = TransitionVisualizerScreen::new(local_app_context); + proof_visualizer_screen = ProofVisualizerScreen::new(local_app_context); + document_visualizer_screen = DocumentVisualizerScreen::new(local_app_context); + contract_visualizer_screen = ContractVisualizerScreen::new(local_app_context); + document_query_screen = DocumentQueryScreen::new(local_app_context); + wallets_balances_screen = WalletsBalancesScreen::new(local_app_context); + proof_log_screen = ProofLogScreen::new(local_app_context); + platform_info_screen = PlatformInfoScreen::new(local_app_context); + tokens_balances_screen = + TokensScreen::new(local_app_context, TokensSubscreen::MyTokens); + token_search_screen = + TokensScreen::new(local_app_context, TokensSubscreen::SearchTokens); + token_creator_screen = + TokensScreen::new(local_app_context, TokensSubscreen::TokenCreator); } // // Create a channel with a buffer size of 32 (adjust as needed) @@ -883,31 +859,3 @@ impl App for AppState { // } } } - -pub(crate) fn detect_dash_qt_path() -> Option { - let path = which::which("dash-qt") - .map(|path| path.to_string_lossy().to_string()) - .inspect_err(|e| tracing::warn!("failed to find dash-qt: {}", e)) - .ok() - .map(PathBuf::from) - .unwrap_or_else(|| { - // Fallback to default paths based on the operating system - if cfg!(target_os = "macos") { - PathBuf::from("/Applications/Dash-Qt.app/Contents/MacOS/Dash-Qt") - } else if cfg!(target_os = "windows") { - // Retrieve the PROGRAMFILES environment variable or default to "C:\\Program Files" - let program_files = std::env::var("PROGRAMFILES") - .unwrap_or_else(|_| "C:\\Program Files".to_string()); - PathBuf::from(program_files).join("DashCore\\dash-qt.exe") - } else { - PathBuf::from("/usr/local/bin/dash-qt") // Default Linux path - } - }); - - if path.is_file() { - Some(path) - } else { - tracing::warn!("Dash-Qt binary not found at: {:?}", path); - None - } -} diff --git a/src/context.rs b/src/context.rs index 6f7ab8a4e..caaa96b85 100644 --- a/src/context.rs +++ b/src/context.rs @@ -8,6 +8,7 @@ use crate::model::contested_name::ContestedName; use crate::model::password_info::PasswordInfo; use crate::model::qualified_contract::QualifiedContract; use crate::model::qualified_identity::{DPNSNameInfo, QualifiedIdentity}; +use crate::model::settings::Settings; use crate::model::wallet::{Wallet, WalletSeedHash}; use crate::sdk_wrapper::initialize_sdk; use crate::ui::RootScreenType; @@ -37,7 +38,6 @@ use dash_sdk::query_types::IndexMap; use egui::Context; use rusqlite::Result; use std::collections::{BTreeMap, HashMap}; -use std::path::PathBuf; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex, RwLock}; @@ -476,21 +476,10 @@ impl AppContext { .insert_or_update_settings(self.network, root_screen_type) } - /// Retrieves the current `RootScreenType` from the settings - #[allow(clippy::type_complexity)] - pub fn get_settings( - &self, - ) -> Result< - Option<( - Network, - RootScreenType, - Option, - Option, - bool, - crate::ui::theme::ThemeMode, - )>, - > { - self.db.get_settings() + /// Retrieves the current settings + pub fn get_settings(&self) -> Result> { + let settings = self.db.get_settings()?.map(Settings::from); + Ok(settings) } /// Retrieves all contracts from the database plus the system contracts from app context. diff --git a/src/model/mod.rs b/src/model/mod.rs index 105cce062..4e3b91988 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -3,4 +3,5 @@ pub mod password_info; pub mod proof_log_item; pub mod qualified_contract; pub mod qualified_identity; +pub mod settings; pub mod wallet; diff --git a/src/model/settings.rs b/src/model/settings.rs new file mode 100644 index 000000000..37b203bc7 --- /dev/null +++ b/src/model/settings.rs @@ -0,0 +1,109 @@ +use crate::model::password_info::PasswordInfo; +use crate::ui::RootScreenType; +use crate::ui::theme::ThemeMode; +use dash_sdk::dpp::dashcore::Network; +use std::path::PathBuf; + +/// Application settings structure +#[derive(Debug, Clone)] +pub struct Settings { + pub network: Network, + pub root_screen_type: RootScreenType, + pub password_info: Option, + /// Path to the Dash-Qt binary, if set. None means autodetect. + /// Empty value (`""`) means path deliberately not set, autodetect will not be performed. + pub dash_qt_path: Option, + pub overwrite_dash_conf: bool, + pub theme_mode: ThemeMode, +} + +impl + From<( + Network, + RootScreenType, + Option, + Option, + bool, + ThemeMode, + )> for Settings +{ + /// Converts a tuple into a Settings instance + /// + /// Used mainly for database operations where settings are retrieved as a tuple. + fn from( + tuple: ( + Network, + RootScreenType, + Option, + Option, + bool, + ThemeMode, + ), + ) -> Self { + Self::new(tuple.0, tuple.1, tuple.2, tuple.3, tuple.4, tuple.5) + } +} + +impl Default for Settings { + /// Default settings for the application + fn default() -> Self { + Self::new( + Network::Dash, + RootScreenType::RootScreenIdentities, + None, + None, // autodetect + true, + ThemeMode::System, + ) + } +} + +impl Settings { + /// Creates a new Settings instance + pub fn new( + network: Network, + root_screen_type: RootScreenType, + password_info: Option, + dash_qt_path: Option, + overwrite_dash_conf: bool, + theme_mode: ThemeMode, + ) -> Self { + Self { + network, + root_screen_type, + password_info, + dash_qt_path: dash_qt_path.or_else(detect_dash_qt_path), + overwrite_dash_conf, + theme_mode, + } + } +} + +/// Detects the path to the Dash-Qt binary on the system +fn detect_dash_qt_path() -> Option { + let path = which::which("dash-qt") + .map(|path| path.to_string_lossy().to_string()) + .inspect_err(|e| tracing::warn!("failed to find dash-qt: {}", e)) + .ok() + .map(PathBuf::from) + .unwrap_or_else(|| { + // Fallback to default paths based on the operating system + if cfg!(target_os = "macos") { + PathBuf::from("/Applications/Dash-Qt.app/Contents/MacOS/Dash-Qt") + } else if cfg!(target_os = "windows") { + // Retrieve the PROGRAMFILES environment variable or default to "C:\\Program Files" + let program_files = std::env::var("PROGRAMFILES") + .unwrap_or_else(|_| "C:\\Program Files".to_string()); + PathBuf::from(program_files).join("DashCore\\dash-qt.exe") + } else { + PathBuf::from("/usr/local/bin/dash-qt") // Default Linux path + } + }); + + if path.is_file() { + Some(path) + } else { + tracing::warn!("Dash-Qt binary not found at: {:?}", path); + None + } +} diff --git a/src/ui/components/dpns_subscreen_chooser_panel.rs b/src/ui/components/dpns_subscreen_chooser_panel.rs index 322ed63d8..42bd73020 100644 --- a/src/ui/components/dpns_subscreen_chooser_panel.rs +++ b/src/ui/components/dpns_subscreen_chooser_panel.rs @@ -17,7 +17,7 @@ pub fn add_dpns_subscreen_chooser_panel(ctx: &Context, app_context: &AppContext) ]; let active_screen = match app_context.get_settings() { - Ok(Some(settings)) => match settings.1 { + Ok(Some(settings)) => match settings.root_screen_type { ui::RootScreenType::RootScreenDPNSActiveContests => DPNSSubscreen::Active, ui::RootScreenType::RootScreenDPNSPastContests => DPNSSubscreen::Past, ui::RootScreenType::RootScreenDPNSOwnedNames => DPNSSubscreen::Owned, diff --git a/src/ui/components/tokens_subscreen_chooser_panel.rs b/src/ui/components/tokens_subscreen_chooser_panel.rs index d5a38a519..e5ef2d119 100644 --- a/src/ui/components/tokens_subscreen_chooser_panel.rs +++ b/src/ui/components/tokens_subscreen_chooser_panel.rs @@ -15,7 +15,7 @@ pub fn add_tokens_subscreen_chooser_panel(ctx: &Context, app_context: &AppContex ]; let active_screen = match app_context.get_settings() { - Ok(Some(settings)) => match settings.1 { + Ok(Some(settings)) => match settings.root_screen_type { ui::RootScreenType::RootScreenMyTokenBalances => TokensSubscreen::MyTokens, ui::RootScreenType::RootScreenTokenSearch => TokensSubscreen::SearchTokens, ui::RootScreenType::RootScreenTokenCreator => TokensSubscreen::TokenCreator, diff --git a/src/ui/components/tools_subscreen_chooser_panel.rs b/src/ui/components/tools_subscreen_chooser_panel.rs index 58e5d8e74..f213865af 100644 --- a/src/ui/components/tools_subscreen_chooser_panel.rs +++ b/src/ui/components/tools_subscreen_chooser_panel.rs @@ -41,7 +41,7 @@ pub fn add_tools_subscreen_chooser_panel(ctx: &Context, app_context: &AppContext ]; let active_screen = match app_context.get_settings() { - Ok(Some(settings)) => match settings.1 { + Ok(Some(settings)) => match settings.root_screen_type { ui::RootScreenType::RootScreenToolsProofLogScreen => ToolsSubscreen::ProofLog, ui::RootScreenType::RootScreenToolsTransitionVisualizerScreen => { ToolsSubscreen::TransactionViewer diff --git a/src/ui/network_chooser_screen.rs b/src/ui/network_chooser_screen.rs index 0952931de..4941eeef7 100644 --- a/src/ui/network_chooser_screen.rs +++ b/src/ui/network_chooser_screen.rs @@ -44,7 +44,6 @@ impl NetworkChooserScreen { devnet_app_context: Option<&Arc>, local_app_context: Option<&Arc>, current_network: Network, - custom_dash_qt_path: Option, overwrite_dash_conf: bool, ) -> Self { let local_network_dashmate_password = if let Ok(config) = Config::load() { @@ -66,13 +65,14 @@ impl NetworkChooserScreen { }; let developer_mode = current_context.is_developer_mode(); - // Load theme preference from settings - let theme_preference = current_context + // Load settings including theme preference and dash_qt_path + let settings = current_context .get_settings() .ok() .flatten() - .map(|(_, _, _, _, _, theme)| theme) - .unwrap_or(ThemeMode::System); + .unwrap_or_default(); + let theme_preference = settings.theme_mode; + let custom_dash_qt_path = settings.dash_qt_path; Self { mainnet_app_context: mainnet_app_context.clone(), @@ -295,7 +295,7 @@ impl NetworkChooserScreen { ) .clicked() { - self.custom_dash_qt_path = None; + self.custom_dash_qt_path = Some(PathBuf::new()); // Reset to empty to avoid auto-detection self.custom_dash_qt_error_message = None; self.save().expect("Expected to save db settings"); } @@ -570,12 +570,18 @@ impl NetworkChooserScreen { } // Add a button to start the network + let start_enabled = if let Some(path) = self.custom_dash_qt_path.as_ref() { + !path.as_os_str().is_empty() && path.is_file() + } else { + false + }; + if network != Network::Regtest { - ui.add_enabled_ui(self.custom_dash_qt_path.is_some(), |ui| { + ui.add_enabled_ui(start_enabled, |ui| { if ui .button("Start") .on_disabled_hover_text( - "Configure dash-qt binary using Advanced Settings below", + "Please select path to dash-qt binary in Advanced Settings", ) .clicked() { @@ -672,12 +678,10 @@ impl ScreenLike for NetworkChooserScreen { self.should_reset_collapsing_states = true; // Reload settings from database to ensure we have the latest values - if let Ok(Some((_, _, _, custom_dash_qt_path, overwrite_dash_conf, theme_preference))) = - self.current_app_context().get_settings() - { - self.custom_dash_qt_path = custom_dash_qt_path; - self.overwrite_dash_conf = overwrite_dash_conf; - self.theme_preference = theme_preference; + if let Ok(Some(settings)) = self.current_app_context().get_settings() { + self.custom_dash_qt_path = settings.dash_qt_path; + self.overwrite_dash_conf = settings.overwrite_dash_conf; + self.theme_preference = settings.theme_mode; } }