Add utility classes for theme management, waiting, watched products, and notifications
- Implement ThemeManager for dynamic light/dark theme switching in the application. - Create Wait class for cancellable delays without exceptions for smoother user experience. - Introduce WatchedProductsStore to manage and persist watched products in JSON format. - Add WindowsNotifier for system notifications to inform users of important events. - Develop ProductViewModel to encapsulate product data and manage UI interactions effectively.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using AutoBidder.Utilities;
|
||||
@@ -21,7 +21,6 @@ namespace AutoBidder
|
||||
|
||||
// Carica impostazioni predefinite aste
|
||||
DefaultBidBeforeDeadlineMs.Text = settings.DefaultBidBeforeDeadlineMs.ToString();
|
||||
DefaultCheckAuctionOpen.IsChecked = settings.DefaultCheckAuctionOpenBeforeBid;
|
||||
DefaultMinPrice.Text = settings.DefaultMinPrice.ToString("F2", System.Globalization.CultureInfo.InvariantCulture);
|
||||
DefaultMaxPrice.Text = settings.DefaultMaxPrice.ToString("F2", System.Globalization.CultureInfo.InvariantCulture);
|
||||
DefaultMaxClicks.Text = settings.DefaultMaxClicks.ToString();
|
||||
@@ -36,11 +35,91 @@ namespace AutoBidder
|
||||
// ?? NUOVO: Carica limite minimo puntate
|
||||
MinimumRemainingBidsTextBox.Text = settings.MinimumRemainingBids.ToString();
|
||||
|
||||
// ?? NUOVO: Carica livello log
|
||||
var logLevelErrorOnly = Settings.FindName("LogLevelErrorOnly") as System.Windows.Controls.RadioButton;
|
||||
var logLevelNormal = Settings.FindName("LogLevelNormal") as System.Windows.Controls.RadioButton;
|
||||
var logLevelInformational = Settings.FindName("LogLevelInformational") as System.Windows.Controls.RadioButton;
|
||||
var logLevelDebug = Settings.FindName("LogLevelDebug") as System.Windows.Controls.RadioButton;
|
||||
var logLevelTrace = Settings.FindName("LogLevelTrace") as System.Windows.Controls.RadioButton;
|
||||
|
||||
switch (settings.MinLogLevel)
|
||||
{
|
||||
case "ErrorOnly":
|
||||
if (logLevelErrorOnly != null) logLevelErrorOnly.IsChecked = true;
|
||||
break;
|
||||
case "Informational":
|
||||
if (logLevelInformational != null) logLevelInformational.IsChecked = true;
|
||||
break;
|
||||
case "Debug":
|
||||
if (logLevelDebug != null) logLevelDebug.IsChecked = true;
|
||||
break;
|
||||
case "Trace":
|
||||
if (logLevelTrace != null) logLevelTrace.IsChecked = true;
|
||||
break;
|
||||
case "Normal":
|
||||
default:
|
||||
if (logLevelNormal != null) logLevelNormal.IsChecked = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Motore di precisione
|
||||
Settings.PollIntervalFarMsTextBox.Text = settings.PollIntervalFarMs.ToString();
|
||||
Settings.PollIntervalMidMsTextBox.Text = settings.PollIntervalMidMs.ToString();
|
||||
Settings.PollIntervalNearMsTextBox.Text = settings.PollIntervalNearMs.ToString();
|
||||
Settings.PollIntervalCriticalMsTextBox.Text = settings.PollIntervalCriticalMs.ToString();
|
||||
Settings.CriticalWindowMsTextBox.Text = settings.CriticalWindowMs.ToString();
|
||||
Settings.MaxRequestsPerSecondTextBox.Text = settings.MaxRequestsPerSecond.ToString("F0", System.Globalization.CultureInfo.InvariantCulture);
|
||||
Settings.PrecisionTimerCheckBox.IsChecked = settings.PrecisionTimerEnabled;
|
||||
|
||||
RefreshEngineDiagnostics();
|
||||
|
||||
// Prodotti seguiti e catalogo
|
||||
Settings.AutoAddEnabledCheckBox.IsChecked = settings.AutoAddProductsEnabled;
|
||||
Settings.AutoAddState = settings.AutoAddNewAuctionState;
|
||||
Settings.AutoAddScanSecondsTextBox.Text = settings.AutoAddScanSeconds.ToString();
|
||||
Settings.AutoAddMaxAuctionsTextBox.Text = settings.AutoAddMaxAuctions.ToString();
|
||||
Settings.AutoAddMaxStartMinutesTextBox.Text = settings.AutoAddMaxStartMinutes.ToString();
|
||||
Settings.AutoAddOnlyNotStartedCheckBox.IsChecked = settings.AutoAddOnlyNotStarted;
|
||||
Settings.SuggestedCoverageTextBox.Text = settings.SuggestedPriceCoveragePercent.ToString("0", System.Globalization.CultureInfo.CurrentCulture);
|
||||
Settings.AverageBidCostTextBox.Text = settings.AverageBidCostEuro.ToString("0.00", System.Globalization.CultureInfo.CurrentCulture);
|
||||
Settings.AutoAddScanDepthTextBox.Text = settings.AutoAddScanMaxAuctions.ToString();
|
||||
Settings.CatalogMaxAuctionsTextBox.Text = settings.CatalogMaxAuctions.ToString();
|
||||
Settings.CatalogAutoRefreshCheckBox.IsChecked = settings.CatalogAutoRefresh;
|
||||
|
||||
// Anticipo, aste programmate, notifiche, cartelle
|
||||
Settings.BidLeadTrackingCheckBox.IsChecked = settings.BidLeadTrackingEnabled;
|
||||
Settings.BidLeadSuggestionsCheckBox.IsChecked = settings.BidLeadSuggestionsEnabled;
|
||||
Settings.BidLeadMinSamplesTextBox.Text = settings.BidLeadMinSamples.ToString();
|
||||
|
||||
Settings.ScheduledBackoffCheckBox.IsChecked = settings.ScheduledAuctionBackoffEnabled;
|
||||
Settings.ScheduledPollFarTextBox.Text = settings.ScheduledPollFarSeconds.ToString();
|
||||
Settings.ScheduledPollMidTextBox.Text = settings.ScheduledPollMidSeconds.ToString();
|
||||
Settings.ScheduledPollWakeTextBox.Text = settings.ScheduledPollWakeSeconds.ToString();
|
||||
|
||||
Settings.NotifyOnWinCheckBox.IsChecked = settings.NotifyOnWin;
|
||||
Settings.NotifyOnLossCheckBox.IsChecked = settings.NotifyOnLoss;
|
||||
|
||||
// I percorsi si mostrano <b>risolti</b>, non come sono salvati: vuoto nel
|
||||
// file significa "predefinito", ma a video il predefinito ha un nome preciso
|
||||
// ed è quello che serve sapere.
|
||||
RefreshDataFolderFields();
|
||||
|
||||
Settings.DetailedStatsCheckBox.IsChecked = settings.DetailedStatsEnabled;
|
||||
Settings.CatalogCacheSecondsTextBox.Text = settings.CatalogCacheSeconds.ToString();
|
||||
|
||||
Settings.WriteAppLogCheckBox.IsChecked = settings.WriteAppLogFile;
|
||||
Settings.WriteFreeBidsLogCheckBox.IsChecked = settings.WriteFreeBidsLogFile;
|
||||
Settings.WriteDossiersCheckBox.IsChecked = settings.WriteAuctionDossiers;
|
||||
Settings.RawPollsCheckBox.IsChecked = settings.DossierIncludeRawPolls;
|
||||
Settings.LogRetentionTextBox.Text = settings.LogRetentionDays.ToString();
|
||||
|
||||
RefreshBidLeadSummary();
|
||||
|
||||
// Aggiorna indicatore visivo
|
||||
UpdateMinBidsIndicator(settings.MinimumRemainingBids);
|
||||
|
||||
// Carica stato iniziale aste
|
||||
// ? NUOVO: Se RememberAuctionStates è attivo, seleziona "Ricorda Stato"
|
||||
// ? NUOVO: Se RememberAuctionStates � attivo, seleziona "Ricorda Stato"
|
||||
if (settings.RememberAuctionStates)
|
||||
{
|
||||
Settings.LoadAuctionsRemember.IsChecked = true;
|
||||
@@ -85,67 +164,6 @@ namespace AutoBidder
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveSettingsButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// ? Carica le impostazioni esistenti per non perdere gli altri valori
|
||||
var settings = Utilities.SettingsManager.Load() ?? new Utilities.AppSettings();
|
||||
|
||||
// === SEZIONE EXPORT: Percorso e Formato ===
|
||||
settings.ExportPath = ExportPathTextBox.Text;
|
||||
settings.LastExportExt = ExtJson.IsChecked == true ? ".json" : ExtXml.IsChecked == true ? ".xml" : ".csv";
|
||||
|
||||
// === SEZIONE EXPORT: Scope (Aste da esportare) ===
|
||||
var cbClosed = this.FindName("ExportClosedToolbar") as System.Windows.Controls.CheckBox;
|
||||
var cbUnknown = this.FindName("ExportUnknownToolbar") as System.Windows.Controls.CheckBox;
|
||||
var cbOpen = this.FindName("ExportOpenToolbar") as System.Windows.Controls.CheckBox;
|
||||
|
||||
var scope = "All";
|
||||
if (cbClosed != null && cbClosed.IsChecked == true) scope = "Closed";
|
||||
else if (cbUnknown != null && cbUnknown.IsChecked == true) scope = "Unknown";
|
||||
else if (cbOpen != null && cbOpen.IsChecked == true) scope = "Open";
|
||||
|
||||
settings.ExportScope = scope;
|
||||
settings.ExportOpen = cbOpen?.IsChecked ?? true;
|
||||
settings.ExportClosed = cbClosed?.IsChecked ?? true;
|
||||
settings.ExportUnknown = cbUnknown?.IsChecked ?? true;
|
||||
|
||||
// === SEZIONE EXPORT: Opzioni ? FIX: Aggiunte le 3 checkbox mancanti ===
|
||||
settings.IncludeOnlyUsedBids = IncludeUsedBids.IsChecked == true;
|
||||
settings.IncludeLogs = IncludeLogs.IsChecked == true;
|
||||
settings.IncludeUserBids = IncludeUserBids.IsChecked == true;
|
||||
settings.IncludeMetadata = IncludeMetadata.IsChecked == true; // ? AGGIUNTO
|
||||
settings.RemoveAfterExport = RemoveAfterExport.IsChecked == true; // ? AGGIUNTO
|
||||
settings.OverwriteExisting = OverwriteExisting.IsChecked == true; // ? AGGIUNTO
|
||||
|
||||
SettingsManager.Save(settings);
|
||||
ExportPreferences.SaveLastExportExtension(settings.LastExportExt);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log($"[ERRORE] Salvataggio impostazioni export: {ex.Message}", LogLevel.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void CancelSettingsButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Ricarica impostazioni export
|
||||
LoadExportSettings();
|
||||
|
||||
// NOTA: Reload cookie RIMOSSO - ora automatico tramite browser
|
||||
|
||||
MessageBox.Show(this, "Impostazioni ripristinate alle ultime salvate.", "Annulla", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log($"[ERRORE] Ripristino impostazioni: {ex.Message}", LogLevel.Error);
|
||||
MessageBox.Show(this, "Errore durante ripristino: " + ex.Message, "Errore", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveDefaultsButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
@@ -164,7 +182,6 @@ namespace AutoBidder
|
||||
return;
|
||||
}
|
||||
|
||||
settings.DefaultCheckAuctionOpenBeforeBid = DefaultCheckAuctionOpen.IsChecked ?? false;
|
||||
|
||||
if (double.TryParse(DefaultMinPrice.Text.Replace(',', '.'), System.Globalization.NumberStyles.Any,
|
||||
System.Globalization.CultureInfo.InvariantCulture, out var minPrice))
|
||||
@@ -239,6 +256,29 @@ namespace AutoBidder
|
||||
Log("[ERRORE] Valore limite minimo puntate non valido (deve essere >= 0)", LogLevel.Error);
|
||||
}
|
||||
|
||||
// ?? NUOVO: Salva livello log
|
||||
var logLevelErrorOnly = Settings.FindName("LogLevelErrorOnly") as System.Windows.Controls.RadioButton;
|
||||
var logLevelNormal = Settings.FindName("LogLevelNormal") as System.Windows.Controls.RadioButton;
|
||||
var logLevelInformational = Settings.FindName("LogLevelInformational") as System.Windows.Controls.RadioButton;
|
||||
var logLevelDebug = Settings.FindName("LogLevelDebug") as System.Windows.Controls.RadioButton;
|
||||
var logLevelTrace = Settings.FindName("LogLevelTrace") as System.Windows.Controls.RadioButton;
|
||||
|
||||
string selectedLogLevel = "Normal"; // Default
|
||||
if (logLevelErrorOnly?.IsChecked == true)
|
||||
selectedLogLevel = "ErrorOnly";
|
||||
else if (logLevelInformational?.IsChecked == true)
|
||||
selectedLogLevel = "Informational";
|
||||
else if (logLevelDebug?.IsChecked == true)
|
||||
selectedLogLevel = "Debug";
|
||||
else if (logLevelTrace?.IsChecked == true)
|
||||
selectedLogLevel = "Trace";
|
||||
else if (logLevelNormal?.IsChecked == true)
|
||||
selectedLogLevel = "Normal";
|
||||
|
||||
settings.MinLogLevel = selectedLogLevel;
|
||||
|
||||
Log($"[LOG] Livello log impostato: {selectedLogLevel}", LogLevel.Info);
|
||||
|
||||
// === SEZIONE DEFAULTS: Stati Iniziali Aste ===
|
||||
var loadAuctionsRemember = Settings.FindName("LoadAuctionsRemember") as System.Windows.Controls.RadioButton;
|
||||
var loadAuctionsActive = Settings.FindName("LoadAuctionsActive") as System.Windows.Controls.RadioButton;
|
||||
@@ -249,7 +289,7 @@ namespace AutoBidder
|
||||
{
|
||||
// Attiva RememberAuctionStates
|
||||
settings.RememberAuctionStates = true;
|
||||
// DefaultStartAuctionsOnLoad diventa irrilevante, ma lo lasciamo a "Stopped" per compatibilità
|
||||
// DefaultStartAuctionsOnLoad diventa irrilevante, ma lo lasciamo a "Stopped" per compatibilit�
|
||||
settings.DefaultStartAuctionsOnLoad = "Stopped";
|
||||
}
|
||||
else
|
||||
@@ -265,10 +305,136 @@ namespace AutoBidder
|
||||
var newAuctionPaused = Settings.FindName("NewAuctionPaused") as System.Windows.Controls.RadioButton;
|
||||
|
||||
settings.DefaultNewAuctionState = newAuctionActive?.IsChecked == true ? "Active" :
|
||||
newAuctionPaused?.IsChecked == true ? "Paused" :
|
||||
newAuctionPaused?.IsChecked == true ? "Paused" :
|
||||
"Stopped";
|
||||
|
||||
|
||||
// === SEZIONE: Motore di precisione ===
|
||||
// Ogni cadenza ha un minimo sensato: sotto i 100 ms si spreca banda senza
|
||||
// guadagnare precisione, perché la puntata la decide il cecchino, non il poll.
|
||||
settings.PollIntervalFarMs = ReadBounded(Settings.PollIntervalFarMsTextBox.Text, settings.PollIntervalFarMs, 200, 30000, "polling lontano");
|
||||
settings.PollIntervalMidMs = ReadBounded(Settings.PollIntervalMidMsTextBox.Text, settings.PollIntervalMidMs, 150, 10000, "polling medio");
|
||||
settings.PollIntervalNearMs = ReadBounded(Settings.PollIntervalNearMsTextBox.Text, settings.PollIntervalNearMs, 100, 5000, "polling vicino");
|
||||
settings.PollIntervalCriticalMs = ReadBounded(Settings.PollIntervalCriticalMsTextBox.Text, settings.PollIntervalCriticalMs, 100, 3000, "polling critico");
|
||||
settings.CriticalWindowMs = ReadBounded(Settings.CriticalWindowMsTextBox.Text, settings.CriticalWindowMs, 1000, 60000, "finestra critica");
|
||||
|
||||
if (double.TryParse(Settings.MaxRequestsPerSecondTextBox.Text.Replace(',', '.'),
|
||||
System.Globalization.NumberStyles.Any,
|
||||
System.Globalization.CultureInfo.InvariantCulture, out var rps) && rps >= 1 && rps <= 200)
|
||||
{
|
||||
settings.MaxRequestsPerSecond = rps;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log("[ERRORE] Limite richieste al secondo non valido (1-200): valore precedente mantenuto", LogLevel.Error);
|
||||
}
|
||||
|
||||
settings.PrecisionTimerEnabled = Settings.PrecisionTimerCheckBox.IsChecked ?? true;
|
||||
|
||||
// === SEZIONE: Prodotti seguiti ===
|
||||
settings.AutoAddProductsEnabled = Settings.AutoAddEnabledCheckBox.IsChecked ?? true;
|
||||
settings.AutoAddNewAuctionState = Settings.AutoAddState;
|
||||
settings.AutoAddScanSeconds = ReadBounded(Settings.AutoAddScanSecondsTextBox.Text,
|
||||
settings.AutoAddScanSeconds, 30, 3600, "intervallo scansione prodotti seguiti", " s");
|
||||
|
||||
if (int.TryParse(Settings.AutoAddMaxAuctionsTextBox.Text, out var autoMax) && autoMax >= 0)
|
||||
settings.AutoAddMaxAuctions = autoMax;
|
||||
else
|
||||
Log("[ERRORE] Massimo aste aggiunte automaticamente non valido (>= 0)", LogLevel.Error);
|
||||
|
||||
settings.AutoAddScanMaxAuctions = ReadBounded(Settings.AutoAddScanDepthTextBox.Text,
|
||||
settings.AutoAddScanMaxAuctions, 50, 6000, "profondita ricerca prodotti seguiti", " aste");
|
||||
|
||||
// 0 = nessun orizzonte, ed e' una scelta vera: per questo il minimo del
|
||||
// controllo di validita' e' zero e non uno.
|
||||
settings.AutoAddOnlyNotStarted = Settings.AutoAddOnlyNotStartedCheckBox.IsChecked ?? false;
|
||||
|
||||
// Limiti consigliati per prodotto: due valori che entrano nel calcolo del
|
||||
// tetto di prezzo. Si accetta la virgola oltre al punto, come nel resto
|
||||
// dell'applicazione: chi scrive "0,20" non deve vedersi rifiutare il valore.
|
||||
if (TryReadDouble(Settings.SuggestedCoverageTextBox.Text, out var copertura) &&
|
||||
copertura is >= 10 and <= 99)
|
||||
settings.SuggestedPriceCoveragePercent = copertura;
|
||||
else
|
||||
Log("[ERRORE] Copertura delle chiusure non valida (10-99%)", LogLevel.Error);
|
||||
|
||||
if (TryReadDouble(Settings.AverageBidCostTextBox.Text, out var costo) &&
|
||||
costo is > 0 and <= 5)
|
||||
settings.AverageBidCostEuro = costo;
|
||||
else
|
||||
Log("[ERRORE] Costo medio di una puntata non valido (0-5 EUR)", LogLevel.Error);
|
||||
|
||||
if (int.TryParse(Settings.AutoAddMaxStartMinutesTextBox.Text, out var orizzonte) && orizzonte >= 0)
|
||||
settings.AutoAddMaxStartMinutes = orizzonte;
|
||||
else
|
||||
Log("[ERRORE] Orizzonte di aggiunta automatica non valido (>= 0 minuti)", LogLevel.Error);
|
||||
|
||||
if (settings.AutoAddProductsEnabled && settings.AutoAddNewAuctionState == "Active")
|
||||
{
|
||||
Log("[ATTENZIONE] Le aste seguite entreranno in stato Attiva: punteranno da sole, spendendo puntate reali.",
|
||||
LogLevel.Warning);
|
||||
}
|
||||
|
||||
// === SEZIONE: Catalogo ===
|
||||
settings.CatalogMaxAuctions = ReadBounded(Settings.CatalogMaxAuctionsTextBox.Text,
|
||||
settings.CatalogMaxAuctions, 20, 5000, "numero massimo aste catalogo", " aste");
|
||||
settings.CatalogAutoRefresh = Settings.CatalogAutoRefreshCheckBox.IsChecked ?? false;
|
||||
settings.CatalogCacheSeconds = ReadBounded(Settings.CatalogCacheSecondsTextBox.Text,
|
||||
settings.CatalogCacheSeconds, 0, 3600, "cache catalogo", " s");
|
||||
|
||||
// === SEZIONE: Anticipo puntata ===
|
||||
settings.BidLeadTrackingEnabled = Settings.BidLeadTrackingCheckBox.IsChecked ?? true;
|
||||
settings.BidLeadSuggestionsEnabled = Settings.BidLeadSuggestionsCheckBox.IsChecked ?? true;
|
||||
settings.BidLeadMinSamples = ReadBounded(Settings.BidLeadMinSamplesTextBox.Text,
|
||||
settings.BidLeadMinSamples, 5, 500, "puntate minime per il consiglio", "");
|
||||
|
||||
// === SEZIONE: Aste programmate ===
|
||||
settings.ScheduledAuctionBackoffEnabled = Settings.ScheduledBackoffCheckBox.IsChecked ?? true;
|
||||
settings.ScheduledPollFarSeconds = ReadBounded(Settings.ScheduledPollFarTextBox.Text,
|
||||
settings.ScheduledPollFarSeconds, 60, 3600, "polling aste lontane", " s");
|
||||
settings.ScheduledPollMidSeconds = ReadBounded(Settings.ScheduledPollMidTextBox.Text,
|
||||
settings.ScheduledPollMidSeconds, 30, 1800, "polling aste vicine all'apertura", " s");
|
||||
settings.ScheduledPollWakeSeconds = ReadBounded(Settings.ScheduledPollWakeTextBox.Text,
|
||||
settings.ScheduledPollWakeSeconds, 30, 600, "margine di risveglio", " s");
|
||||
|
||||
// === SEZIONE: Notifiche ===
|
||||
settings.NotifyOnWin = Settings.NotifyOnWinCheckBox.IsChecked ?? true;
|
||||
settings.NotifyOnLoss = Settings.NotifyOnLossCheckBox.IsChecked ?? false;
|
||||
|
||||
// === SEZIONE: Cartelle dei dati ===
|
||||
var previousData = AppPaths.DataFolder;
|
||||
var previousStats = AppPaths.StatsFolder;
|
||||
|
||||
// Il campo contiene il percorso risolto: se coincide con il predefinito si
|
||||
// salva vuoto, così spostando la cartella dati i registri la seguono invece
|
||||
// di restare inchiodati a un percorso che nessuno ha mai scelto davvero.
|
||||
settings.DataFolder = NormalizeFolderChoice(Settings.DataFolderTextBox.Text, AppPaths.DataFolder, settings.DataFolder);
|
||||
settings.StatsFolder = NormalizeFolderChoice(Settings.StatsFolderTextBox.Text, AppPaths.StatsFolder, settings.StatsFolder);
|
||||
settings.LogFolder = NormalizeFolderChoice(Settings.LogFolderTextBox.Text, AppPaths.LogFolder, settings.LogFolder);
|
||||
|
||||
settings.DetailedStatsEnabled = Settings.DetailedStatsCheckBox.IsChecked ?? true;
|
||||
|
||||
settings.WriteAppLogFile = Settings.WriteAppLogCheckBox.IsChecked ?? true;
|
||||
settings.WriteFreeBidsLogFile = Settings.WriteFreeBidsLogCheckBox.IsChecked ?? true;
|
||||
settings.WriteAuctionDossiers = Settings.WriteDossiersCheckBox.IsChecked ?? true;
|
||||
settings.DossierIncludeRawPolls = Settings.RawPollsCheckBox.IsChecked ?? true;
|
||||
|
||||
if (int.TryParse(Settings.LogRetentionTextBox.Text?.Trim(), out var retention) && retention >= 0)
|
||||
settings.LogRetentionDays = retention;
|
||||
|
||||
Utilities.SettingsManager.Save(settings);
|
||||
|
||||
ApplyDataFolderSettings(settings, previousData, previousStats);
|
||||
|
||||
// Il limite di ritmo si applica a caldo, senza ricreare il trasporto.
|
||||
_auctionMonitor.ApplyTransportSettings(settings);
|
||||
ApplyProductWatchSettings(settings);
|
||||
|
||||
// L'interruttore in Esplora non deve dissentire dalle impostazioni, e il
|
||||
// ciclo va fermato o riavviato di conseguenza.
|
||||
Browser.SetAutoRefresh(settings.CatalogAutoRefresh);
|
||||
if (settings.CatalogAutoRefresh) StartCatalogAutoRefresh();
|
||||
else StopCatalogAutoRefresh();
|
||||
RefreshEngineDiagnostics();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -276,6 +442,64 @@ namespace AutoBidder
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Legge un intero entro limiti, mantenendo il valore precedente se il testo non è
|
||||
/// utilizzabile: un campo sbagliato non deve far ripartire il motore a caso.
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
/// Legge un numero con la virgola o col punto. Le impostazioni si scrivono a mano
|
||||
/// in un'applicazione italiana: rifiutare "0,20" sarebbe un dispetto.
|
||||
/// </summary>
|
||||
private static bool TryReadDouble(string? text, out double value)
|
||||
{
|
||||
value = 0;
|
||||
if (string.IsNullOrWhiteSpace(text)) return false;
|
||||
|
||||
return double.TryParse(text.Trim().Replace(',', '.'),
|
||||
System.Globalization.NumberStyles.Any,
|
||||
System.Globalization.CultureInfo.InvariantCulture, out value);
|
||||
}
|
||||
|
||||
private int ReadBounded(string text, int current, int min, int max, string label, string unit = "ms")
|
||||
{
|
||||
if (int.TryParse(text, out var value) && value >= min && value <= max)
|
||||
return value;
|
||||
|
||||
Log($"[ERRORE] Valore {label} non valido ({min}-{max}{unit}): mantenuto {current}{unit}", LogLevel.Error);
|
||||
return current;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mostra lo stato del motore: aggancio all'orologio del server e traffico prodotto.
|
||||
/// </summary>
|
||||
private void RefreshEngineDiagnostics()
|
||||
{
|
||||
try
|
||||
{
|
||||
var box = Settings.EngineDiagnosticsText;
|
||||
if (box == null) return;
|
||||
|
||||
var clock = _auctionMonitor.Clock;
|
||||
var sent = _auctionMonitor.RequestsSent;
|
||||
var failed = _auctionMonitor.RequestsFailed;
|
||||
|
||||
// Attenzione a come si presenta lo scarto: Bidoo dichiara i secondi interi,
|
||||
// quindi i campioni si distribuiscono per forza su una finestra di circa
|
||||
// 1000 ms. Non è l'errore della stima — la stima usa il minimo, che converge
|
||||
// al confine reale del secondo. Un valore molto oltre i 1000 ms segnala
|
||||
// invece una rete instabile.
|
||||
var clockLine = clock.IsSynced
|
||||
? $"Orologio server agganciato su {clock.SampleCount} campioni: le scadenze seguono il server, non l'orologio locale.\n" +
|
||||
$"Finestra campioni {clock.SpreadMs:F0} ms (intorno a 1000 ms è normale: Bidoo dichiara i secondi interi)."
|
||||
: $"Orologio server in sincronizzazione ({clock.SampleCount}/3 campioni): finché non è agganciato la scadenza è stimata localmente.";
|
||||
|
||||
box.Text = $"{clockLine}\n" +
|
||||
$"Richieste inviate: {sent:N0} — fallite: {failed:N0}.\n" +
|
||||
$"Motori attivi: {_auctionMonitor.ActiveRunners}.";
|
||||
}
|
||||
catch { /* la diagnostica non deve mai far fallire il salvataggio */ }
|
||||
}
|
||||
|
||||
private void CancelDefaultsButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
@@ -290,5 +514,43 @@ namespace AutoBidder
|
||||
MessageBox.Show(this, "Errore durante ripristino: " + ex.Message, "Errore", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
// === HANDLER PER PULSANTI UNIFICATI ===
|
||||
|
||||
private void SaveAllSettings_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Salva tutte le impostazioni (ora solo defaults, export rimosso)
|
||||
SaveDefaultsButton_Click(sender, e);
|
||||
|
||||
MessageBox.Show(
|
||||
"Tutte le impostazioni sono state salvate con successo.\n\nLe nuove impostazioni verranno applicate alle aste future.",
|
||||
"Impostazioni Salvate",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Information
|
||||
);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log($"[ERRORE] Salvataggio impostazioni: {ex.Message}", LogLevel.Error);
|
||||
MessageBox.Show(this, "Errore durante salvataggio: " + ex.Message, "Errore", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void CancelAllSettings_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Annulla tutte le modifiche
|
||||
LoadDefaultSettings();
|
||||
MessageBox.Show(this, "Impostazioni ripristinate alle ultime salvate.", "Annulla", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log($"[ERRORE] Ripristino impostazioni: {ex.Message}", LogLevel.Error);
|
||||
MessageBox.Show(this, "Errore durante ripristino: " + ex.Message, "Errore", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user