Persist the two console output display preferences and add a second toolbar toggle for monochrome text. - Persistence: new Settings fields console_line_accents / console_text_color (both default true, matching the ConsoleTab statics so an upgrade re-save can't flip visible behavior). Restored into the statics at startup in the App constructor, and saved from a diff-and-save after the console renders (only when a value actually changed — save() is disk I/O). The startup restore also fixes a pre-existing bug: scanline was only synced from settings when the Settings page or first-run wizard ran, so an existing wallet ignored a saved scanline preference on launch. - Monochrome text: a new toolbar toggle (FORMAT_COLOR_TEXT, dimmed when off) next to the accent toggle. channelTextColor() early-returns the neutral COLOR_RESULT for every channel (and JSON syntax role) when off, leaving the left accent bars independent. COLOR_RESULT is contrast-floored per theme, so text stays readable on light and dark terminals. Adds console_toggle_text_color to the English source + all 8 translations. Verified: both toggles independent, persistence round-trips across restart, monochrome readable on light + dark skins. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
576 lines
28 KiB
C++
576 lines
28 KiB
C++
// DragonX Wallet - ImGui Edition
|
||
// Copyright 2024-2026 The Hush Developers
|
||
// Released under the GPLv3
|
||
|
||
#pragma once
|
||
|
||
#include <algorithm>
|
||
#include <cstddef>
|
||
#include <map>
|
||
#include <string>
|
||
#include <set>
|
||
#include <vector>
|
||
|
||
namespace dragonx {
|
||
namespace config {
|
||
|
||
/**
|
||
* @brief Application settings manager
|
||
*
|
||
* Handles loading and saving of user preferences.
|
||
*/
|
||
class Settings {
|
||
public:
|
||
Settings();
|
||
~Settings();
|
||
|
||
/**
|
||
* @brief Load settings from default location
|
||
* @return true if loaded successfully
|
||
*/
|
||
bool load();
|
||
|
||
/**
|
||
* @brief Load settings from specific path
|
||
* @param path Path to settings file
|
||
* @return true if loaded successfully
|
||
*/
|
||
bool load(const std::string& path);
|
||
|
||
/**
|
||
* @brief Save settings to default location
|
||
* @return true if saved successfully
|
||
*/
|
||
bool save();
|
||
|
||
/**
|
||
* @brief Save settings to specific path
|
||
* @param path Path to settings file
|
||
* @return true if saved successfully
|
||
*/
|
||
bool save(const std::string& path);
|
||
|
||
/**
|
||
* @brief Get default settings file path
|
||
*/
|
||
static std::string getDefaultPath();
|
||
|
||
enum class LiteServerSelectionPreferenceMode {
|
||
Sticky,
|
||
Random
|
||
};
|
||
|
||
// Pool selection mode for the mining tab: Manual (user picks the pool) or
|
||
// AutoBalance (the wallet spreads miners across the official pools by hashrate).
|
||
enum class PoolSelectMode {
|
||
Manual,
|
||
AutoBalance
|
||
};
|
||
|
||
struct LiteServerPreference {
|
||
std::string url;
|
||
std::string label;
|
||
bool enabled = true;
|
||
};
|
||
|
||
// A user-defined portfolio entry: a custom label tied to a group of wallet addresses.
|
||
// The Market tab's portfolio card sums these addresses' balances under the label.
|
||
struct PortfolioEntry {
|
||
std::string label;
|
||
std::vector<std::string> addresses;
|
||
std::string icon; // project_icons wallet-icon name; empty = no icon
|
||
unsigned int color = 0; // packed IM_COL32 accent; 0 = theme default
|
||
int outlineOpacity = 25; // accent-outline opacity, percent (0-100)
|
||
// Per-group price data. priceBasis: 0 = live market (USD), 1 = live market (BTC),
|
||
// 2 = DRGX only (no fiat value), 3 = manual price. Defaults preserve prior behavior
|
||
// (show DRGX + USD value).
|
||
int priceBasis = 0;
|
||
double manualPrice = 0.0; // price per DRGX for the Manual basis
|
||
std::string manualCurrency = "USD";
|
||
bool showDrgx = true; // show the DRGX amount on the card
|
||
bool showValue = true; // show the converted/fiat value on the card
|
||
bool show24h = false; // show the 24h % change (live-market bases only)
|
||
bool showSparkline = false; // show a price-trend sparkline (live-market bases only)
|
||
int sparklineInterval = 0; // 0=minute 1=hour 2=day 3=week 4=month (resample of price history)
|
||
// Per-wallet visibility: "" (shown in every wallet — legacy/global) or a wallet-identity
|
||
// hash (shown only when that wallet is active). New entries are tagged with the current
|
||
// wallet so a portfolio built for wallet A doesn't clutter wallet B.
|
||
std::string scope;
|
||
// Legacy dashboard-grid placement (deprecated by the row layout; kept for back-compat so an
|
||
// older build's saved positions aren't dropped, but no longer used by the renderer).
|
||
int gridCol = -1;
|
||
int gridRow = -1;
|
||
int gridW = 8;
|
||
int gridH = 3;
|
||
};
|
||
|
||
// Theme
|
||
std::string getTheme() const { return theme_; }
|
||
void setTheme(const std::string& theme) { theme_ = theme; }
|
||
|
||
// Unified skin
|
||
std::string getSkinId() const { return skin_id_; }
|
||
void setSkinId(const std::string& id) { skin_id_ = id; }
|
||
|
||
// Stable z-address chosen for HushChat: the reply-to address in outgoing headers, and (for
|
||
// legacy/non-mnemonic wallets) the source of the seed-derived chat identity. Persisted so the
|
||
// identity + reply address don't shift when new addresses are generated.
|
||
std::string getChatReplyZaddr() const { return chat_reply_zaddr_; }
|
||
void setChatReplyZaddr(const std::string& z) { chat_reply_zaddr_ = z; }
|
||
|
||
// Privacy
|
||
bool getSaveZtxs() const { return save_ztxs_; }
|
||
void setSaveZtxs(bool save) { save_ztxs_ = save; }
|
||
|
||
bool getAutoShield() const { return auto_shield_; }
|
||
void setAutoShield(bool shield) { auto_shield_ = shield; }
|
||
|
||
bool getUseTor() const { return use_tor_; }
|
||
void setUseTor(bool tor) { use_tor_ = tor; }
|
||
|
||
// Fees
|
||
bool getAllowCustomFees() const { return allow_custom_fees_; }
|
||
void setAllowCustomFees(bool allow) { allow_custom_fees_ = allow; }
|
||
|
||
double getDefaultFee() const { return default_fee_; }
|
||
void setDefaultFee(double fee) { default_fee_ = fee; }
|
||
|
||
// Price
|
||
bool getFetchPrices() const { return fetch_prices_; }
|
||
void setFetchPrices(bool fetch) { fetch_prices_ = fetch; }
|
||
|
||
// Explorer URLs
|
||
std::string getTxExplorerUrl() const { return tx_explorer_url_; }
|
||
void setTxExplorerUrl(const std::string& url) { tx_explorer_url_ = url; }
|
||
|
||
std::string getAddressExplorerUrl() const { return address_explorer_url_; }
|
||
void setAddressExplorerUrl(const std::string& url) { address_explorer_url_ = url; }
|
||
|
||
// Language
|
||
std::string getLanguage() const { return language_; }
|
||
void setLanguage(const std::string& lang) { language_ = lang; }
|
||
|
||
// Visual effects
|
||
bool getAcrylicEnabled() const { return acrylic_enabled_; }
|
||
void setAcrylicEnabled(bool v) { acrylic_enabled_ = v; }
|
||
|
||
int getAcrylicQuality() const { return acrylic_quality_; }
|
||
void setAcrylicQuality(int v) { acrylic_quality_ = v; }
|
||
|
||
float getBlurMultiplier() const { return blur_multiplier_; }
|
||
void setBlurMultiplier(float v) { blur_multiplier_ = v; }
|
||
|
||
bool getReducedTransparency() const { return ui_opacity_ >= 0.99f; }
|
||
void setReducedTransparency(bool v) { if (v) ui_opacity_ = 1.0f; }
|
||
|
||
float getUIOpacity() const { return ui_opacity_; }
|
||
void setUIOpacity(float v) { ui_opacity_ = std::max(0.3f, std::min(1.0f, v)); }
|
||
|
||
float getWindowOpacity() const { return window_opacity_; }
|
||
void setWindowOpacity(float v) { window_opacity_ = std::max(0.3f, std::min(1.0f, v)); }
|
||
|
||
float getNoiseOpacity() const { return noise_opacity_; }
|
||
void setNoiseOpacity(float v) { noise_opacity_ = v; }
|
||
|
||
// Gradient background mode (use gradient variant of theme background)
|
||
bool getGradientBackground() const { return gradient_background_; }
|
||
void setGradientBackground(bool v) { gradient_background_ = v; }
|
||
|
||
// Balance layout (string ID, e.g. "classic", "donut")
|
||
std::string getBalanceLayout() const { return balance_layout_; }
|
||
void setBalanceLayout(const std::string& v) { balance_layout_ = v; }
|
||
|
||
// Market-tab portfolio row style: 0 = single-line, 1 = two-line, 2 = value-hero. Cycled with
|
||
// Left/Right arrows on the Market tab (like the Overview layouts).
|
||
int getPortfolioStyle() const { return portfolio_style_; }
|
||
void setPortfolioStyle(int v) { portfolio_style_ = (v < 0 || v > 2) ? 0 : v; }
|
||
|
||
// Console scanline effect
|
||
bool getScanlineEnabled() const { return scanline_enabled_; }
|
||
void setScanlineEnabled(bool v) { scanline_enabled_ = v; }
|
||
|
||
// Console output appearance: per-line left color accent bars, and per-channel text coloring.
|
||
// (Defaults match the ConsoleTab statics so an upgrade re-save doesn't flip visible behavior.)
|
||
bool getConsoleLineAccents() const { return console_line_accents_; }
|
||
void setConsoleLineAccents(bool v) { console_line_accents_ = v; }
|
||
bool getConsoleTextColor() const { return console_text_color_; }
|
||
void setConsoleTextColor(bool v) { console_text_color_ = v; }
|
||
|
||
// Hidden addresses (addresses hidden from the UI by the user)
|
||
const std::set<std::string>& getHiddenAddresses() const { return hidden_addresses_; }
|
||
bool isAddressHidden(const std::string& addr) const { return hidden_addresses_.count(addr) > 0; }
|
||
void hideAddress(const std::string& addr) { hidden_addresses_.insert(addr); }
|
||
void unhideAddress(const std::string& addr) { hidden_addresses_.erase(addr); }
|
||
int getHiddenAddressCount() const { return (int)hidden_addresses_.size(); }
|
||
|
||
// Favorite addresses (pinned to top of address list)
|
||
const std::set<std::string>& getFavoriteAddresses() const { return favorite_addresses_; }
|
||
bool isAddressFavorite(const std::string& addr) const { return favorite_addresses_.count(addr) > 0; }
|
||
void favoriteAddress(const std::string& addr) { favorite_addresses_.insert(addr); }
|
||
void unfavoriteAddress(const std::string& addr) { favorite_addresses_.erase(addr); }
|
||
int getFavoriteAddressCount() const { return (int)favorite_addresses_.size(); }
|
||
|
||
// Address metadata (labels, icons, custom ordering)
|
||
struct AddressMeta {
|
||
std::string label;
|
||
std::string icon; // material icon name, e.g. "savings"
|
||
int sortOrder = -1; // -1 = auto (use default sort)
|
||
bool mining = false;
|
||
};
|
||
const AddressMeta& getAddressMeta(const std::string& addr) const {
|
||
static const AddressMeta empty{};
|
||
auto it = address_meta_.find(addr);
|
||
return it != address_meta_.end() ? it->second : empty;
|
||
}
|
||
void setAddressLabel(const std::string& addr, const std::string& label) {
|
||
address_meta_[addr].label = label;
|
||
}
|
||
void setAddressIcon(const std::string& addr, const std::string& icon) {
|
||
address_meta_[addr].icon = icon;
|
||
}
|
||
void setAddressSortOrder(const std::string& addr, int order) {
|
||
address_meta_[addr].sortOrder = order;
|
||
}
|
||
bool isMiningAddress(const std::string& addr) const {
|
||
auto it = address_meta_.find(addr);
|
||
return it != address_meta_.end() && it->second.mining;
|
||
}
|
||
void setMiningAddress(const std::string& addr, bool mining) {
|
||
address_meta_[addr].mining = mining;
|
||
}
|
||
std::set<std::string> getMiningAddresses() const {
|
||
std::set<std::string> addresses;
|
||
for (const auto& [addr, meta] : address_meta_) {
|
||
if (meta.mining) addresses.insert(addr);
|
||
}
|
||
return addresses;
|
||
}
|
||
int getNextSortOrder() const {
|
||
int mx = -1;
|
||
for (const auto& [k, v] : address_meta_)
|
||
if (v.sortOrder > mx) mx = v.sortOrder;
|
||
return mx + 1;
|
||
}
|
||
void swapAddressOrder(const std::string& a, const std::string& b) {
|
||
int oa = address_meta_[a].sortOrder;
|
||
int ob = address_meta_[b].sortOrder;
|
||
address_meta_[a].sortOrder = ob;
|
||
address_meta_[b].sortOrder = oa;
|
||
}
|
||
|
||
// First-run wizard
|
||
bool getWizardCompleted() const { return wizard_completed_; }
|
||
void setWizardCompleted(bool v) { wizard_completed_ = v; }
|
||
|
||
// Whether the one-time "back up your seed phrase" reminder has already been shown.
|
||
bool getSeedBackupReminded() const { return seed_backup_reminded_; }
|
||
void setSeedBackupReminded(bool v) { seed_backup_reminded_ = v; }
|
||
|
||
// Bundled-daemon size we last prompted to install (see App::renderDaemonUpdatePrompt). Lets the
|
||
// "a newer node is bundled — update?" prompt fire once per wallet version, never re-nagging.
|
||
long long getDaemonUpdatePromptedSize() const { return daemon_update_prompted_size_; }
|
||
void setDaemonUpdatePromptedSize(long long v) { daemon_update_prompted_size_ = v; }
|
||
|
||
// Active wallet file the daemon loads via -wallet=<name> (multi-wallet). A plain filename in
|
||
// the datadir; defaults to the daemon's own default. Used at launch to know which wallet we're
|
||
// on before connect + to scope per-wallet data.
|
||
std::string getActiveWalletFile() const { return active_wallet_file_; }
|
||
void setActiveWalletFile(const std::string& v) { active_wallet_file_ = v; }
|
||
|
||
// Pending "migrate to a seed wallet" state (Phase 1 created the wallet; a later sweep/adopt
|
||
// step consumes it). dest = the new wallet's sweep-target z-address; tempDir = its datadir.
|
||
bool getSeedMigrationPending() const { return seed_migration_pending_; }
|
||
void setSeedMigrationPending(bool v) { seed_migration_pending_ = v; }
|
||
std::string getSeedMigrationDest() const { return seed_migration_dest_; }
|
||
void setSeedMigrationDest(const std::string& v) { seed_migration_dest_ = v; }
|
||
std::string getSeedMigrationTempDir() const { return seed_migration_temp_dir_; }
|
||
void setSeedMigrationTempDir(const std::string& v) { seed_migration_temp_dir_ = v; }
|
||
// The sweep transaction id, persisted once the sweep is submitted — non-empty means the
|
||
// migration is past the sweep, so a resume goes to the confirm/adopt stage (not sweep again).
|
||
std::string getSeedMigrationSweepTxid() const { return seed_migration_sweep_txid_; }
|
||
void setSeedMigrationSweepTxid(const std::string& v) { seed_migration_sweep_txid_ = v; }
|
||
|
||
// Security — auto-lock timeout (seconds; 0 = disabled)
|
||
int getAutoLockTimeout() const { return auto_lock_timeout_; }
|
||
void setAutoLockTimeout(int seconds) { auto_lock_timeout_ = seconds; }
|
||
|
||
// Security — unlock duration (seconds) for walletpassphrase
|
||
int getUnlockDuration() const { return unlock_duration_; }
|
||
void setUnlockDuration(int seconds) { unlock_duration_ = seconds; }
|
||
|
||
// Security — PIN unlock enabled
|
||
bool getPinEnabled() const { return pin_enabled_; }
|
||
void setPinEnabled(bool v) { pin_enabled_ = v; }
|
||
|
||
// Daemon — keep running in background when closing the app
|
||
bool getKeepDaemonRunning() const { return keep_daemon_running_; }
|
||
void setKeepDaemonRunning(bool v) { keep_daemon_running_ = v; }
|
||
|
||
// Daemon — stop externally-started daemons on exit (default: false)
|
||
bool getStopExternalDaemon() const { return stop_external_daemon_; }
|
||
void setStopExternalDaemon(bool v) { stop_external_daemon_ = v; }
|
||
|
||
// Daemon — maximum peer connections (0 = daemon default)
|
||
int getMaxConnections() const { return max_connections_; }
|
||
void setMaxConnections(int v) { max_connections_ = std::max(0, v); }
|
||
|
||
// Lite wallet server selection
|
||
LiteServerSelectionPreferenceMode getLiteServerSelectionMode() const { return lite_server_selection_mode_; }
|
||
void setLiteServerSelectionMode(LiteServerSelectionPreferenceMode mode) { lite_server_selection_mode_ = mode; }
|
||
std::string getLiteStickyServerUrl() const { return lite_sticky_server_url_; }
|
||
void setLiteStickyServerUrl(const std::string& url) { lite_sticky_server_url_ = url; }
|
||
std::string getLiteChainName() const { return lite_chain_name_; }
|
||
void setLiteChainName(const std::string& chainName) { lite_chain_name_ = chainName; }
|
||
std::size_t getLiteRandomSelectionSeed() const { return lite_random_selection_seed_; }
|
||
void setLiteRandomSelectionSeed(std::size_t seed) { lite_random_selection_seed_ = seed; }
|
||
bool getLitePersistSelectedServer() const { return lite_persist_selected_server_; }
|
||
void setLitePersistSelectedServer(bool persist) { lite_persist_selected_server_ = persist; }
|
||
const std::vector<LiteServerPreference>& getLiteServers() const { return lite_servers_; }
|
||
void setLiteServers(const std::vector<LiteServerPreference>& servers) { lite_servers_ = servers; }
|
||
|
||
// User-defined portfolio entries (Market tab). "All funds" is implicit, not stored here.
|
||
const std::vector<PortfolioEntry>& getPortfolioEntries() const { return portfolio_entries_; }
|
||
void setPortfolioEntries(const std::vector<PortfolioEntry>& entries) { portfolio_entries_ = entries; }
|
||
|
||
// Lite servers the user has hidden from the Network tab (kept by URL, shown via a toggle).
|
||
const std::set<std::string>& getLiteHiddenServers() const { return lite_hidden_servers_; }
|
||
bool isLiteServerHidden(const std::string& url) const { return lite_hidden_servers_.count(url) > 0; }
|
||
void hideLiteServer(const std::string& url) { lite_hidden_servers_.insert(url); }
|
||
void unhideLiteServer(const std::string& url) { lite_hidden_servers_.erase(url); }
|
||
|
||
// Lite wallet rollout / kill-switch (see wallet/lite_rollout_policy.h).
|
||
// Override: "auto" (honor rollout manifest), "force_on", or "force_off".
|
||
std::string getLiteRolloutOverride() const { return lite_rollout_override_; }
|
||
void setLiteRolloutOverride(const std::string& v) { lite_rollout_override_ = v; }
|
||
// Stable, locally-generated install id used only to derive the staged-rollout bucket.
|
||
// Never transmitted; carries no PII. Generated on first use if empty.
|
||
std::string getLiteInstallId() const { return lite_install_id_; }
|
||
void setLiteInstallId(const std::string& v) { lite_install_id_ = v; }
|
||
|
||
// Verbose diagnostic logging (connection attempts, daemon state, port owner, etc.)
|
||
bool getVerboseLogging() const { return verbose_logging_; }
|
||
void setVerboseLogging(bool v) { verbose_logging_ = v; }
|
||
|
||
// Daemon — debug logging categories
|
||
const std::set<std::string>& getDebugCategories() const { return debug_categories_; }
|
||
void setDebugCategories(const std::set<std::string>& cats) { debug_categories_ = cats; }
|
||
bool hasDebugCategory(const std::string& cat) const { return debug_categories_.count(cat) > 0; }
|
||
void toggleDebugCategory(const std::string& cat) {
|
||
if (debug_categories_.count(cat)) debug_categories_.erase(cat);
|
||
else debug_categories_.insert(cat);
|
||
}
|
||
|
||
// Visual effects — animated theme effects (hue cycling, shimmer, glow, etc.)
|
||
bool getThemeEffectsEnabled() const { return theme_effects_enabled_; }
|
||
void setThemeEffectsEnabled(bool v) { theme_effects_enabled_ = v; }
|
||
|
||
// Low-spec mode — disables heavy visual effects for better performance
|
||
bool getLowSpecMode() const { return low_spec_mode_; }
|
||
void setLowSpecMode(bool v) { low_spec_mode_ = v; }
|
||
|
||
// Reduce motion — disables animated transitions for accessibility
|
||
bool getReduceMotion() const { return reduce_motion_; }
|
||
void setReduceMotion(bool v) { reduce_motion_ = v; }
|
||
|
||
// Market — last selected exchange + pair
|
||
std::string getSelectedExchange() const { return selected_exchange_; }
|
||
void setSelectedExchange(const std::string& v) { selected_exchange_ = v; }
|
||
std::string getSelectedPair() const { return selected_pair_; }
|
||
void setSelectedPair(const std::string& v) { selected_pair_ = v; }
|
||
int getChartInterval() const { return chart_interval_; } // Market chart range 0=Live..4=1M
|
||
void setChartInterval(int v) { chart_interval_ = v; }
|
||
int getChartStyle() const { return chart_style_; } // 0 = line, 1 = candlestick
|
||
void setChartStyle(int v) { chart_style_ = v; }
|
||
|
||
// Pool mining
|
||
std::string getPoolUrl() const { return pool_url_; }
|
||
void setPoolUrl(const std::string& v) { pool_url_ = v; }
|
||
std::string getPoolAlgo() const { return pool_algo_; }
|
||
void setPoolAlgo(const std::string& v) { pool_algo_ = v; }
|
||
std::string getPoolWorker() const { return pool_worker_; }
|
||
void setPoolWorker(const std::string& v) { pool_worker_ = v; }
|
||
int getPoolThreads() const { return pool_threads_; }
|
||
void setPoolThreads(int v) { pool_threads_ = v; }
|
||
bool getPoolTls() const { return pool_tls_; }
|
||
void setPoolTls(bool v) { pool_tls_ = v; }
|
||
bool getPoolHugepages() const { return pool_hugepages_; }
|
||
void setPoolHugepages(bool v) { pool_hugepages_ = v; }
|
||
bool getPoolMode() const { return pool_mode_; }
|
||
void setPoolMode(bool v) { pool_mode_ = v; }
|
||
PoolSelectMode getPoolSelectMode() const { return pool_select_mode_; }
|
||
void setPoolSelectMode(PoolSelectMode v) { pool_select_mode_ = v; }
|
||
|
||
// Installed DRG-XMRig release tag (for in-app miner update detection); empty if unknown/bundled.
|
||
std::string getXmrigVersion() const { return xmrig_version_; }
|
||
void setXmrigVersion(const std::string& v) { xmrig_version_ = v; }
|
||
|
||
// Mine when idle (auto-start mining when system is idle)
|
||
bool getMineWhenIdle() const { return mine_when_idle_; }
|
||
void setMineWhenIdle(bool v) { mine_when_idle_ = v; }
|
||
int getMineIdleDelay() const { return mine_idle_delay_; }
|
||
void setMineIdleDelay(int seconds) { mine_idle_delay_ = std::max(30, seconds); }
|
||
|
||
// Idle thread scaling — scale thread count instead of start/stop
|
||
bool getIdleThreadScaling() const { return idle_thread_scaling_; }
|
||
void setIdleThreadScaling(bool v) { idle_thread_scaling_ = v; }
|
||
int getIdleThreadsActive() const { return idle_threads_active_; }
|
||
void setIdleThreadsActive(int v) { idle_threads_active_ = std::max(0, v); }
|
||
int getIdleThreadsIdle() const { return idle_threads_idle_; }
|
||
void setIdleThreadsIdle(int v) { idle_threads_idle_ = std::max(0, v); }
|
||
bool getIdleGpuAware() const { return idle_gpu_aware_; }
|
||
void setIdleGpuAware(bool v) { idle_gpu_aware_ = v; }
|
||
|
||
// Saved pool URLs (user-managed favorites dropdown)
|
||
const std::vector<std::string>& getSavedPoolUrls() const { return saved_pool_urls_; }
|
||
void addSavedPoolUrl(const std::string& url) {
|
||
// Don't add duplicates
|
||
for (const auto& u : saved_pool_urls_) if (u == url) return;
|
||
saved_pool_urls_.push_back(url);
|
||
}
|
||
void removeSavedPoolUrl(const std::string& url) {
|
||
saved_pool_urls_.erase(
|
||
std::remove(saved_pool_urls_.begin(), saved_pool_urls_.end(), url),
|
||
saved_pool_urls_.end());
|
||
}
|
||
|
||
// Saved pool worker addresses (user-managed favorites dropdown)
|
||
const std::vector<std::string>& getSavedPoolWorkers() const { return saved_pool_workers_; }
|
||
void addSavedPoolWorker(const std::string& addr) {
|
||
for (const auto& a : saved_pool_workers_) if (a == addr) return;
|
||
saved_pool_workers_.push_back(addr);
|
||
}
|
||
void removeSavedPoolWorker(const std::string& addr) {
|
||
saved_pool_workers_.erase(
|
||
std::remove(saved_pool_workers_.begin(), saved_pool_workers_.end(), addr),
|
||
saved_pool_workers_.end());
|
||
}
|
||
|
||
// Font scale (user accessibility setting, 1.0–1.5)
|
||
float getFontScale() const { return font_scale_; }
|
||
void setFontScale(float v) { font_scale_ = std::max(1.0f, std::min(1.5f, v)); }
|
||
|
||
// Window size persistence (logical pixels at 1x scale)
|
||
int getWindowWidth() const { return window_width_; }
|
||
int getWindowHeight() const { return window_height_; }
|
||
void setWindowSize(int w, int h) { window_width_ = w; window_height_ = h; }
|
||
|
||
// Returns true once after an upgrade (version mismatch detected on load)
|
||
bool needsUpgradeSave() const { return needs_upgrade_save_; }
|
||
void clearUpgradeSave() { needs_upgrade_save_ = false; }
|
||
|
||
private:
|
||
std::string settings_path_;
|
||
|
||
// Settings values
|
||
std::string theme_ = "dragonx";
|
||
std::string skin_id_ = "dragonx";
|
||
std::string chat_reply_zaddr_;
|
||
bool save_ztxs_ = true;
|
||
bool auto_shield_ = true;
|
||
bool use_tor_ = false;
|
||
bool allow_custom_fees_ = false;
|
||
double default_fee_ = 0.0001;
|
||
bool fetch_prices_ = true;
|
||
std::string tx_explorer_url_ = "https://explorer.dragonx.is/tx/";
|
||
std::string address_explorer_url_ = "https://explorer.dragonx.is/address/";
|
||
std::string language_ = "en";
|
||
bool acrylic_enabled_ = true;
|
||
int acrylic_quality_ = 2;
|
||
float blur_multiplier_ = 0.10f;
|
||
float noise_opacity_ = 0.5f;
|
||
bool gradient_background_ = false;
|
||
#ifdef _WIN32
|
||
float ui_opacity_ = 0.50f; // Card/sidebar opacity (0.3–1.0, 1.0 = opaque)
|
||
float window_opacity_ = 0.90f; // Background alpha (0.3–1.0, <1 = desktop visible)
|
||
#else
|
||
float ui_opacity_ = 1.0f; // Mac/Linux: default fully opaque
|
||
float window_opacity_ = 1.0f; // Mac/Linux: default fully opaque
|
||
#endif
|
||
std::string balance_layout_ = "classic";
|
||
int portfolio_style_ = 0; // Market portfolio row style (0 single / 1 two-line / 2 hero)
|
||
bool scanline_enabled_ = true;
|
||
bool console_line_accents_ = true; // left color accent bars in console output
|
||
bool console_text_color_ = true; // per-channel text coloring in console output
|
||
std::set<std::string> hidden_addresses_;
|
||
std::set<std::string> favorite_addresses_;
|
||
std::map<std::string, AddressMeta> address_meta_;
|
||
bool wizard_completed_ = false;
|
||
bool seed_backup_reminded_ = false;
|
||
long long daemon_update_prompted_size_ = 0; // bundled daemon size last offered via the update prompt
|
||
std::string active_wallet_file_ = "wallet.dat"; // -wallet=<name> the daemon loads (multi-wallet)
|
||
bool seed_migration_pending_ = false;
|
||
std::string seed_migration_dest_;
|
||
std::string seed_migration_temp_dir_;
|
||
std::string seed_migration_sweep_txid_;
|
||
int auto_lock_timeout_ = 900; // 15 minutes
|
||
int unlock_duration_ = 600; // 10 minutes
|
||
bool pin_enabled_ = false;
|
||
bool keep_daemon_running_ = false;
|
||
bool stop_external_daemon_ = false;
|
||
int max_connections_ = 0; // 0 = daemon default
|
||
|
||
// Lite wallet server preferences. These are user/server settings only;
|
||
// wallet secrets, wallet files, and lifecycle state are never stored here.
|
||
LiteServerSelectionPreferenceMode lite_server_selection_mode_ = LiteServerSelectionPreferenceMode::Sticky;
|
||
std::string lite_sticky_server_url_ = "https://lite.dragonx.is";
|
||
std::string lite_chain_name_ = "main"; // SDXL backend chain id; must be main/test/regtest
|
||
std::size_t lite_random_selection_seed_ = 0;
|
||
bool lite_persist_selected_server_ = true;
|
||
std::string lite_rollout_override_ = "auto"; // auto|force_on|force_off
|
||
std::string lite_install_id_; // random local-only id; rollout-bucket source
|
||
std::vector<LiteServerPreference> lite_servers_ = {
|
||
{"https://lite.dragonx.is", "DragonX Lite", true},
|
||
{"https://lite1.dragonx.is", "DragonX Lite 1", true},
|
||
{"https://lite2.dragonx.is", "DragonX Lite 2", true},
|
||
{"https://lite3.dragonx.is", "DragonX Lite 3", true},
|
||
{"https://lite4.dragonx.is", "DragonX Lite 4", true},
|
||
{"https://lite5.dragonx.is", "DragonX Lite 5", true}
|
||
};
|
||
std::set<std::string> lite_hidden_servers_; // server URLs hidden from the Network tab
|
||
|
||
std::vector<PortfolioEntry> portfolio_entries_; // Market tab custom portfolio groups
|
||
|
||
bool verbose_logging_ = false;
|
||
std::set<std::string> debug_categories_;
|
||
bool theme_effects_enabled_ = true;
|
||
bool low_spec_mode_ = false;
|
||
bool reduce_motion_ = false;
|
||
std::string selected_exchange_ = "Nonkyc.io";
|
||
std::string selected_pair_ = "DRGX/USDT";
|
||
int chart_interval_ = 4; // Market chart range (0=Live 1=1H 2=1D 3=1W 4=1M)
|
||
int chart_style_ = 1; // Market chart style (0=line, 1=candlestick)
|
||
|
||
// Pool mining
|
||
std::string pool_url_ = "pool.dragonx.is:3433";
|
||
std::string pool_algo_ = "rx/hush";
|
||
std::string pool_worker_ = "";
|
||
int pool_threads_ = 0;
|
||
bool pool_tls_ = false;
|
||
bool pool_hugepages_ = true;
|
||
bool pool_mode_ = false; // false=solo, true=pool
|
||
PoolSelectMode pool_select_mode_ = PoolSelectMode::Manual; // manual vs auto-balance pool choice
|
||
std::string xmrig_version_; // installed DRG-XMRig release tag (update detection)
|
||
bool mine_when_idle_ = false; // auto-start mining when system idle
|
||
int mine_idle_delay_= 120; // seconds of idle before mining starts
|
||
bool idle_thread_scaling_ = false; // scale threads instead of start/stop
|
||
int idle_threads_active_ = 0; // threads when user active (0 = auto)
|
||
int idle_threads_idle_ = 0; // threads when idle (0 = auto = all)
|
||
bool idle_gpu_aware_ = true; // treat GPU activity as non-idle
|
||
std::vector<std::string> saved_pool_urls_; // user-saved pool URL favorites
|
||
std::vector<std::string> saved_pool_workers_; // user-saved worker address favorites
|
||
|
||
// Font scale (user accessibility, 1.0–3.0; 1.0 = default)
|
||
float font_scale_ = 1.0f;
|
||
|
||
// Window size (logical pixels at 1x scale; 0 = use default 1200×775)
|
||
int window_width_ = 0;
|
||
int window_height_ = 0;
|
||
|
||
// Wallet version that last wrote this settings file (for upgrade detection)
|
||
std::string settings_version_;
|
||
bool needs_upgrade_save_ = false; // true when version changed
|
||
};
|
||
|
||
} // namespace config
|
||
} // namespace dragonx
|