Aggiornamento alla versione 4.0.0

- Aggiunta dipendenza per WebView2 per integrare un browser.
- Introdotto layout a schede (TabControl) per organizzare le funzionalità.
- Aggiunto browser WebView2 per navigazione e aggiunta aste.
- Implementata gestione delle impostazioni di esportazione (CSV, JSON, XML).
- Aggiunta funzionalità di caricamento e analisi delle aste chiuse.
- Introdotta gestione dei cookie di sessione tramite la scheda "Impostazioni".
- Creato controllo personalizzato `SimpleToolbar` per layout modulare.
- Migliorata gestione dello stato utente e fallback per dati mancanti.
- Rimossi stili e animazioni obsolete per semplificare il codice.
- Salvate le impostazioni utente in un file JSON locale.
- Correzioni di bug e miglioramenti di leggibilità del codice.
This commit is contained in:
Alberto Balbo
2025-11-04 23:05:49 +01:00
parent 967005b96a
commit 8717a3b6ef
7 changed files with 1274 additions and 537 deletions

View File

@@ -23,6 +23,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.0" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.1343.22" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.6584" /> <PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.6584" />
</ItemGroup> </ItemGroup>

View File

@@ -0,0 +1,16 @@
<UserControl x:Class="AutoBidder.Controls.SimpleToolbar"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="48" d:DesignWidth="800">
<Grid Background="Transparent">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ContentPresenter Content="{Binding LeftContent, RelativeSource={RelativeSource AncestorType=UserControl}}" VerticalAlignment="Center" />
<ContentPresenter Grid.Column="1" Content="{Binding RightContent, RelativeSource={RelativeSource AncestorType=UserControl}}" VerticalAlignment="Center" />
</Grid>
</UserControl>

View File

@@ -0,0 +1,28 @@
using System.Windows;
using System.Windows.Controls;
namespace AutoBidder.Controls
{
public partial class SimpleToolbar : UserControl
{
public static readonly DependencyProperty LeftContentProperty = DependencyProperty.Register("LeftContent", typeof(object), typeof(SimpleToolbar));
public static readonly DependencyProperty RightContentProperty = DependencyProperty.Register("RightContent", typeof(object), typeof(SimpleToolbar));
public object? LeftContent
{
get => GetValue(LeftContentProperty);
set => SetValue(LeftContentProperty, value);
}
public object? RightContent
{
get => GetValue(RightContentProperty);
set => SetValue(RightContentProperty, value);
}
public SimpleToolbar()
{
InitializeComponent();
}
}
}

View File

@@ -2,6 +2,7 @@
<Window x:Class="AutoBidder.Dialogs.AddAuctionSimpleDialog" <Window x:Class="AutoBidder.Dialogs.AddAuctionSimpleDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:AutoBidder.Controls"
Title="Aggiungi Asta" Height="320" Width="700" Title="Aggiungi Asta" Height="320" Width="700"
Background="#0a0a0a" Foreground="#FFFFFF" Background="#0a0a0a" Foreground="#FFFFFF"
WindowStartupLocation="CenterOwner" WindowStartupLocation="CenterOwner"
@@ -24,12 +25,16 @@
Padding="8" FontSize="13" ToolTip="Inserisci uno o pi&#x00F9; URL/ID dell'asta. Separali con a capo, spazio o ';'" Padding="8" FontSize="13" ToolTip="Inserisci uno o pi&#x00F9; URL/ID dell'asta. Separali con a capo, spazio o ';'"
AcceptsReturn="True" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto" Height="160" /> AcceptsReturn="True" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto" Height="160" />
<StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,8,0,0"> <controls:SimpleToolbar Grid.Row="3" Margin="0,8,0,0">
<Button x:Name="OkButton" Content="OK" Width="110" Margin="6" Padding="10,8" <controls:SimpleToolbar.RightContent>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button Content="OK" Width="110" Margin="6" Padding="10,8"
Style="{StaticResource SmallButtonStyle}" Background="#00CC66" Foreground="White" Click="OkButton_Click" /> Style="{StaticResource SmallButtonStyle}" Background="#00CC66" Foreground="White" Click="OkButton_Click" />
<Button x:Name="CancelButton" Content="Annulla" Width="110" Margin="6" Padding="10,8" <Button Content="Annulla" Width="110" Margin="6" Padding="10,8"
Style="{StaticResource SmallButtonStyle}" Background="#666" Foreground="White" Click="CancelButton_Click" /> Style="{StaticResource SmallButtonStyle}" Background="#666" Foreground="White" Click="CancelButton_Click" />
</StackPanel> </StackPanel>
</controls:SimpleToolbar.RightContent>
</controls:SimpleToolbar>
</Grid> </Grid>
</Border> </Border>
</Window> </Window>

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,11 @@
using System.Collections.ObjectModel; using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Text.Json;
using System.Collections.Generic;
using System.Text;
using System.Net; using System.Net;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
@@ -19,9 +26,9 @@ namespace AutoBidder
/// </summary> /// </summary>
public partial class MainWindow : Window public partial class MainWindow : Window
{ {
// SERVIZI CORE // SERVIZI CORE
private readonly AuctionMonitor _auctionMonitor; private readonly AuctionMonitor _auctionMonitor;
private readonly ObservableCollection<AuctionViewModel> _auctionViewModels = new(); private readonly System.Collections.ObjectModel.ObservableCollection<AuctionViewModel> _auctionViewModels = new System.Collections.ObjectModel.ObservableCollection<AuctionViewModel>();
// UI State // UI State
private AuctionViewModel? _selectedAuction; private AuctionViewModel? _selectedAuction;
private bool _isAutomationActive = false; private bool _isAutomationActive = false;
@@ -40,6 +47,9 @@ namespace AutoBidder
private System.Windows.Threading.DispatcherTimer _userBannerTimer; private System.Windows.Threading.DispatcherTimer _userBannerTimer;
private System.Windows.Threading.DispatcherTimer _userHtmlTimer; private System.Windows.Threading.DispatcherTimer _userHtmlTimer;
// export cancellation
private CancellationTokenSource? _exportCts;
public MainWindow() public MainWindow()
{ {
InitializeComponent(); InitializeComponent();
@@ -71,6 +81,9 @@ namespace AutoBidder
// Carica aste salvate // Carica aste salvate
LoadSavedAuctions(); LoadSavedAuctions();
// Load export/settings UI
LoadExportSettings();
// Ensure initial global button states (pause/stop disabled until starting) // Ensure initial global button states (pause/stop disabled until starting)
UpdateGlobalControlButtons(); UpdateGlobalControlButtons();
@@ -84,7 +97,7 @@ namespace AutoBidder
_userBannerTimer.Start(); _userBannerTimer.Start();
_ = UpdateUserBannerInfoAsync(); _ = UpdateUserBannerInfoAsync();
// Timer per aggiornamento dati utente da HTML ogni 3 minuti // Timer per aggiornamento dati utente da HTML ogni3 minuti
_userHtmlTimer = new System.Windows.Threading.DispatcherTimer(); _userHtmlTimer = new System.Windows.Threading.DispatcherTimer();
_userHtmlTimer.Interval = TimeSpan.FromMinutes(3); _userHtmlTimer.Interval = TimeSpan.FromMinutes(3);
_userHtmlTimer.Tick += UserHtmlTimer_Tick; _userHtmlTimer.Tick += UserHtmlTimer_Tick;
@@ -482,7 +495,7 @@ namespace AutoBidder
{ {
if (_selectedAuction != null && sender is TextBox tb) if (_selectedAuction != null && sender is TextBox tb)
{ {
if (int.TryParse(tb.Text, out var value) && value >= 0 && value <= 8) if (int.TryParse(tb.Text, out var value) && value >=0 && value <=8)
{ {
_selectedAuction.TimerClick = value; _selectedAuction.TimerClick = value;
} }
@@ -493,7 +506,7 @@ namespace AutoBidder
{ {
if (_selectedAuction != null && sender is TextBox tb) if (_selectedAuction != null && sender is TextBox tb)
{ {
if (int.TryParse(tb.Text, out var value) && value >= 0) if (int.TryParse(tb.Text, out var value) && value >=0)
{ {
_selectedAuction.AuctionInfo.DelayMs = value; _selectedAuction.AuctionInfo.DelayMs = value;
} }
@@ -528,7 +541,7 @@ namespace AutoBidder
{ {
if (_selectedAuction != null && sender is TextBox tb) if (_selectedAuction != null && sender is TextBox tb)
{ {
if (int.TryParse(tb.Text, out var value) && value >= 0) if (int.TryParse(tb.Text, out var value) && value >=0)
{ {
_selectedAuction.AuctionInfo.MinResets = value; _selectedAuction.AuctionInfo.MinResets = value;
} }
@@ -539,7 +552,7 @@ namespace AutoBidder
{ {
if (_selectedAuction != null && sender is TextBox tb) if (_selectedAuction != null && sender is TextBox tb)
{ {
if (int.TryParse(tb.Text, out var value) && value >= 0) if (int.TryParse(tb.Text, out var value) && value >=0)
{ {
_selectedAuction.AuctionInfo.MaxResets = value; _selectedAuction.AuctionInfo.MaxResets = value;
} }
@@ -550,7 +563,7 @@ namespace AutoBidder
{ {
if (_selectedAuction != null && sender is TextBox tb) if (_selectedAuction != null && sender is TextBox tb)
{ {
if (int.TryParse(tb.Text, out var value) && value >= 0) if (int.TryParse(tb.Text, out var value) && value >=0)
{ {
_selectedAuction.MaxClicks = value; _selectedAuction.MaxClicks = value;
SaveAuctions(); // Persist change immediately SaveAuctions(); // Persist change immediately
@@ -570,12 +583,12 @@ namespace AutoBidder
if (result == MessageBoxResult.Yes) if (result == MessageBoxResult.Yes)
{ {
_selectedAuction.TimerClick = 0; _selectedAuction.TimerClick =0;
_selectedAuction.AuctionInfo.DelayMs = 50; _selectedAuction.AuctionInfo.DelayMs =50;
_selectedAuction.MinPrice = 0; _selectedAuction.MinPrice =0;
_selectedAuction.MaxPrice = 0; _selectedAuction.MaxPrice =0;
_selectedAuction.AuctionInfo.MinResets = 0; _selectedAuction.AuctionInfo.MinResets =0;
_selectedAuction.AuctionInfo.MaxResets = 0; _selectedAuction.AuctionInfo.MaxResets =0;
UpdateSelectedAuctionDetails(_selectedAuction); UpdateSelectedAuctionDetails(_selectedAuction);
Log($"Reset impostazioni: {_selectedAuction.Name}"); Log($"Reset impostazioni: {_selectedAuction.Name}");
@@ -1112,8 +1125,198 @@ namespace AutoBidder
Log($"[ERRORE] Errore salvataggio: {ex.Message}"); Log($"[ERRORE] Errore salvataggio: {ex.Message}");
} }
} }
private enum LogLevel { Info, Warn, Error } // Add LoadExportSettings to initialize UI from saved settings
private void LoadExportSettings()
{
try
{
var s = Utilities.SettingsManager.Load();
if (s != null)
{
ExportPathTextBox.Text = s.ExportPath ?? string.Empty;
if (!string.IsNullOrEmpty(s.LastExportExt))
{
var ext = s.LastExportExt.ToLowerInvariant();
if (ext == ".json") ExtJson.IsChecked = true;
else if (ext == ".xml") ExtXml.IsChecked = true;
else ExtCsv.IsChecked = true;
}
else
{
ExtCsv.IsChecked = true;
}
switch (s.ExportScope)
{
case "Closed": ExportScopeCombo.SelectedIndex =1; break;
case "Unknown": ExportScopeCombo.SelectedIndex =2; break;
default: ExportScopeCombo.SelectedIndex =0; break;
}
IncludeOnlyUsedBids.IsChecked = s.IncludeOnlyUsedBids;
IncludeLogs.IsChecked = s.IncludeLogs;
IncludeUserBids.IsChecked = s.IncludeUserBids;
}
}
catch { }
finally
{
try { ExportProgressBar.Visibility = Visibility.Collapsed; ExportProgressText.Visibility = Visibility.Collapsed; } catch { }
}
}
// Export all (simple async wrapper)
private async void ExportAllButton_Click(object sender, RoutedEventArgs e)
{
try
{
var settings = Utilities.SettingsManager.Load();
string ext = ExtJson.IsChecked == true ? ".json" : ExtXml.IsChecked == true ? ".xml" : ".csv";
var dlg = new Microsoft.Win32.SaveFileDialog() { FileName = "auctions_export" + ext, Filter = "CSV files|*.csv|JSON files|*.json|XML files|*.xml|All files|*.*" };
if (dlg.ShowDialog(this) != true) return;
var path = dlg.FileName;
var all = _auctionMonitor.GetAuctions();
var selection = all.AsEnumerable();
var scope = settings.ExportScope ?? "All";
if (scope == "Closed") selection = selection.Where(a => !a.IsActive);
else if (scope == "Unknown") selection = selection.Where(a => (a.BidHistory == null || a.BidHistory.Count ==0) && (a.BidderStats == null || a.BidderStats.Count ==0));
var list = selection.ToList();
if (list.Count ==0)
{
MessageBox.Show(this, "Nessuna asta da esportare.", "Esporta Aste", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
ExportProgressBar.Visibility = Visibility.Visible;
ExportProgressText.Visibility = Visibility.Visible;
ExportProgressText.Text = "Esportazione in corso...";
await Task.Run(() =>
{
if (path.EndsWith(".json", StringComparison.OrdinalIgnoreCase))
{
var json = System.Text.Json.JsonSerializer.Serialize(list, new System.Text.Json.JsonSerializerOptions { WriteIndented = true });
System.IO.File.WriteAllText(path, json, System.Text.Encoding.UTF8);
}
else if (path.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
{
var doc = new XDocument(new XElement("Auctions",
from a in list
select new XElement("Auction",
new XElement("AuctionId", a.AuctionId),
new XElement("Name", a.Name),
new XElement("OriginalUrl", a.OriginalUrl ?? string.Empty)
)
));
doc.Save(path);
}
else
{
// Use existing exporter
CsvExporter.ExportAllAuctions(list, path);
}
});
try { ExportPreferences.SaveLastExportExtension(System.IO.Path.GetExtension(path)); } catch { }
MessageBox.Show(this, "Esportazione completata.", "Esporta Aste", MessageBoxButton.OK, MessageBoxImage.Information);
Log($"[EXPORT] Aste esportate -> {path}");
}
catch (Exception ex)
{
Log($"[ERRORE] Esportazione massiva: {ex.Message}");
MessageBox.Show(this, "Errore durante esportazione: " + ex.Message, "Esporta Aste", MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
ExportProgressBar.Visibility = Visibility.Collapsed;
ExportProgressText.Visibility = Visibility.Collapsed;
}
}
// Browser handlers (simple)
private void BrowserBackButton_Click(object sender, RoutedEventArgs e)
{
try { if (EmbeddedWebView?.CoreWebView2 != null && EmbeddedWebView.CoreWebView2.CanGoBack) EmbeddedWebView.CoreWebView2.GoBack(); } catch { }
}
private void BrowserForwardButton_Click(object sender, RoutedEventArgs e)
{
try { if (EmbeddedWebView?.CoreWebView2 != null && EmbeddedWebView.CoreWebView2.CanGoForward) EmbeddedWebView.CoreWebView2.GoForward(); } catch { }
}
private void BrowserRefreshButton_Click(object sender, RoutedEventArgs e)
{
try { EmbeddedWebView?.Reload(); } catch { }
}
private void BrowserHomeButton_Click(object sender, RoutedEventArgs e)
{
try { EmbeddedWebView?.CoreWebView2?.Navigate("https://it.bidoo.com/"); BrowserAddress.Text = "https://it.bidoo.com/"; } catch { }
}
private void BrowserGoButton_Click(object sender, RoutedEventArgs e)
{
try
{
var url = BrowserAddress.Text?.Trim(); if (string.IsNullOrEmpty(url)) return; if (!url.StartsWith("http", StringComparison.OrdinalIgnoreCase)) url = "https://" + url;
EmbeddedWebView?.CoreWebView2?.Navigate(url);
}
catch { }
}
private void BrowserAddAuctionButton_Click(object sender, RoutedEventArgs e)
{
try { var url = BrowserAddress.Text?.Trim() ?? EmbeddedWebView?.Source?.ToString(); if (!string.IsNullOrEmpty(url)) _ = AddAuctionFromUrl(url); } catch { }
}
private void BrowserContext_AddAuction_Click(object sender, RoutedEventArgs e)
{
try { var url = EmbeddedWebView?.Source?.ToString() ?? BrowserAddress.Text; if (!string.IsNullOrEmpty(url)) _ = AddAuctionFromUrl(url); } catch { }
}
private void EmbeddedWebView_NavigationStarting(object sender, Microsoft.Web.WebView2.Core.CoreWebView2NavigationStartingEventArgs e)
{
try { BrowserAddress.Text = e.Uri ?? string.Empty; BrowserAddAuctionButton.IsEnabled = IsValidAuctionUrl(e.Uri ?? string.Empty); } catch { }
}
private void EmbeddedWebView_NavigationCompleted(object sender, Microsoft.Web.WebView2.Core.CoreWebView2NavigationCompletedEventArgs e)
{
try { var uri = EmbeddedWebView?.Source?.ToString() ?? BrowserAddress.Text; BrowserAddress.Text = uri; BrowserAddAuctionButton.IsEnabled = IsValidAuctionUrl(uri); } catch { }
}
private void ExportBrowseButton_Click(object sender, RoutedEventArgs e)
{
var dlg = new Microsoft.Win32.SaveFileDialog() { FileName = "export.csv", Filter = "CSV files|*.csv|All files|*.*" };
if (dlg.ShowDialog(this) == true)
{
ExportPathTextBox.Text = System.IO.Path.GetDirectoryName(dlg.FileName) ?? string.Empty;
}
}
private void SaveSettingsButton_Click(object sender, RoutedEventArgs e)
{
try
{
var s = new Utilities.AppSettings()
{
ExportPath = ExportPathTextBox.Text,
LastExportExt = ExtJson.IsChecked == true ? ".json" : ExtXml.IsChecked == true ? ".xml" : ".csv",
ExportScope = ExportScopeCombo.SelectedIndex ==1 ? "Closed" : ExportScopeCombo.SelectedIndex ==2 ? "Unknown" : "All",
IncludeOnlyUsedBids = IncludeOnlyUsedBids.IsChecked == true,
IncludeLogs = IncludeLogs.IsChecked == true,
IncludeUserBids = IncludeUserBids.IsChecked == true
};
Utilities.SettingsManager.Save(s);
ExportPreferences.SaveLastExportExtension(s.LastExportExt);
MessageBox.Show(this, "Impostazioni salvate.", "Salva", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
MessageBox.Show(this, "Errore salvataggio impostazioni: " + ex.Message, "Errore", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void CancelSettingsButton_Click(object sender, RoutedEventArgs e)
{
LoadExportSettings();
}
private enum LogLevel { Info, Warn, Error }
private void Log(string message, LogLevel level = LogLevel.Info) private void Log(string message, LogLevel level = LogLevel.Info)
{ {
@@ -1170,6 +1373,8 @@ namespace AutoBidder
base.OnClosed(e); base.OnClosed(e);
} }
// NOTE: Window_Loaded and ToggleTabsButton handlers were removed because they forced a fixed
// width on `MainTabControl` at runtime, causing the UI to render incorrectly compared to the designer.
// === HANDLER BOTTONI GRIGLIA === // === HANDLER BOTTONI GRIGLIA ===
private void GridOpenAuction_Click(object sender, RoutedEventArgs e) private void GridOpenAuction_Click(object sender, RoutedEventArgs e)
@@ -1248,7 +1453,7 @@ namespace AutoBidder
private void PauseAllButton_Click(object sender, RoutedEventArgs e) private void PauseAllButton_Click(object sender, RoutedEventArgs e)
{ {
int paused = 0; int paused =0;
foreach (var vm in _auctionViewModels) foreach (var vm in _auctionViewModels)
{ {
if (vm.IsActive && !vm.IsPaused) if (vm.IsActive && !vm.IsPaused)
@@ -1283,31 +1488,31 @@ namespace AutoBidder
// According to rule: darken if all in same state that matches the button meaning // According to rule: darken if all in same state that matches the button meaning
if (allActive) if (allActive)
{ {
StartButton.IsEnabled = false; StartButton.Opacity = 0.5; StartButton.IsEnabled = false; StartButton.Opacity =0.5;
} }
else else
{ {
StartButton.IsEnabled = true; StartButton.Opacity = 1.0; StartButton.IsEnabled = true; StartButton.Opacity =1.0;
} }
// PauseAll button: darken if allPaused or allStopped // PauseAll button: darken if allPaused or allStopped
if (allPaused || allStopped) if (allPaused || allStopped)
{ {
PauseAllButton.IsEnabled = false; PauseAllButton.Opacity = 0.5; PauseAllButton.IsEnabled = false; PauseAllButton.Opacity =0.5;
} }
else else
{ {
PauseAllButton.IsEnabled = true; PauseAllButton.Opacity = 1.0; PauseAllButton.IsEnabled = true; PauseAllButton.Opacity =1.0;
} }
// Stop button: darken if allStopped // Stop button: darken if allStopped
if (allStopped) if (allStopped)
{ {
StopButton.IsEnabled = false; StopButton.Opacity = 0.5; StopButton.IsEnabled = false; StopButton.Opacity =0.5;
} }
else else
{ {
StopButton.IsEnabled = true; StopButton.Opacity = 1.0; StopButton.IsEnabled = true; StopButton.Opacity =1.0;
} }
} }
@@ -1321,13 +1526,32 @@ namespace AutoBidder
var info = await _auctionMonitor.GetUserBannerInfoAsync(); var info = await _auctionMonitor.GetUserBannerInfoAsync();
if (info != null) if (info != null)
{ {
BannerAsteDaRiscattare.Text = $"{info.nAsteVinte - info.nAsteConfermate}"; // Map banner info to available UI fields
// BannerPuntateBonus.Text = $"Bonus: {info.nPuntateBonus}"; // RIMOSSO try
{
// Use nPuntateDaRiscattare if available as remaining bids proxy
if (info.nPuntateDaRiscattare >0)
{
RemainingBidsText.Text = info.nPuntateDaRiscattare.ToString();
}
else if (info.nPuntateBonus >0)
{
RemainingBidsText.Text = info.nPuntateBonus.ToString();
}
else
{
// fallback: show total won minus confirmed if meaningful
if (info.nAsteVinte >= info.nAsteConfermate)
RemainingBidsText.Text = (info.nAsteVinte - info.nAsteConfermate).ToString();
else
RemainingBidsText.Text = "--";
}
}
catch { RemainingBidsText.Text = "--"; }
} }
else else
{ {
BannerAsteDaRiscattare.Text = "--"; RemainingBidsText.Text = "--";
// BannerPuntateBonus.Text = "Bonus: --"; // RIMOSSO
} }
} }
@@ -1358,9 +1582,9 @@ namespace AutoBidder
} }
else else
{ {
// Rimuovi newline e taglia a max 20 caratteri // Rimuovi newline e taglia a max20 caratteri
var clean = username.Replace("\r", "").Replace("\n", ""); var clean = username.Replace("\r", "").Replace("\n", "");
UsernameText.Text = clean.Length > 20 ? clean.Substring(0, 20) + "..." : clean; UsernameText.Text = clean.Length >20 ? clean.Substring(0,20) + "..." : clean;
} }
RemainingBidsText.Text = remainingBids.ToString(); RemainingBidsText.Text = remainingBids.ToString();
} }
@@ -1377,7 +1601,7 @@ namespace AutoBidder
{ {
var dlg = new Microsoft.Win32.SaveFileDialog() var dlg = new Microsoft.Win32.SaveFileDialog()
{ {
Filter = "CSV files|*.csv|JSON files|*.json|XML files|*.xml|All files|*.*", Filter = "CSV files|*.csv|JSON files|*.json|All files|*.*",
FileName = $"auction_{_selectedAuction.AuctionId}.csv" FileName = $"auction_{_selectedAuction.AuctionId}.csv"
}; };
@@ -1538,5 +1762,168 @@ namespace AutoBidder
if (v == null) return string.Empty; if (v == null) return string.Empty;
return v.Replace("\"", "\"\""); return v.Replace("\"", "\"\"");
} }
private void OpenFreeBids_Click(object sender, RoutedEventArgs e)
{
try { MainTabControl.SelectedIndex =1; } catch { }
}
private void ExportSelectionButton_Click(object sender, RoutedEventArgs e)
{
ExportSelectedAuction_Click(sender, e);
}
// New handlers for Statistics tab
private async void LoadClosedAuctionsButton_Click(object sender, RoutedEventArgs e)
{
try
{
StatsStatusText.Text = "Caricamento aste chiuse in corso...";
Log("[STATS] Avvio caricamento aste chiuse...");
// Use the existing ClosedAuctionsScraper service
var scraper = new Services.ClosedAuctionsScraper(null, null, (msg) => Log($"[SCRAPER] {msg}"));
var closedUrl = "https://it.bidoo.com/closed_auctions.php";
var results = new System.Collections.ObjectModel.ObservableCollection<Models.ClosedAuctionRecord>();
await foreach (var rec in scraper.ScrapeYieldAsync(closedUrl))
{
// Filter out records without bids info
if (!rec.BidsUsed.HasValue)
{
continue;
}
results.Add(rec);
StatsStatusText.Text = $"Caricate {results.Count} aste...";
}
StatsDataGrid.ItemsSource = results;
StatsStatusText.Text = $"Totale: {results.Count} aste caricate";
Log($"[STATS] Caricamento completato: {results.Count} aste");
}
catch (Exception ex)
{
Log($"[ERRORE] Caricamento statistiche: {ex.Message}", LogLevel.Error);
StatsStatusText.Text = "Errore nel caricamento";
MessageBox.Show($"Errore: {ex.Message}", "Errore Caricamento", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private async void ExportStatsButton_Click(object sender, RoutedEventArgs e)
{
try
{
var items = StatsDataGrid.ItemsSource as System.Collections.ObjectModel.ObservableCollection<Models.ClosedAuctionRecord>;
if (items == null || items.Count ==0)
{
MessageBox.Show("Nessuna statistica da esportare. Carica prima le aste chiuse.", "Info", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
var dlg = new Microsoft.Win32.SaveFileDialog()
{
Filter = "CSV files|*.csv|JSON files|*.json|All files|*.*",
FileName = "closed_auctions_stats.csv"
};
if (dlg.ShowDialog(this) != true) return;
var path = dlg.FileName;
await Task.Run(() =>
{
if (path.EndsWith(".json", StringComparison.OrdinalIgnoreCase))
{
var json = System.Text.Json.JsonSerializer.Serialize(items, new System.Text.Json.JsonSerializerOptions { WriteIndented = true });
System.IO.File.WriteAllText(path, json, System.Text.Encoding.UTF8);
}
else
{
// CSV export
using var sw = new System.IO.StreamWriter(path, false, System.Text.Encoding.UTF8);
sw.WriteLine("ProductName,FinalPrice,Winner,BidsUsed,AuctionUrl");
foreach (var item in items)
{
sw.WriteLine($"\"{EscapeCsv(item.ProductName)}\",{item.FinalPrice:F2},\"{EscapeCsv(item.Winner)}\",{item.BidsUsed},\"{EscapeCsv(item.AuctionUrl)}\"");
}
}
});
MessageBox.Show("Esportazione completata.", "Esporta Statistiche", MessageBoxButton.OK, MessageBoxImage.Information);
Log($"[EXPORT] Statistiche esportate -> {path}");
}
catch (Exception ex)
{
Log($"[ERRORE] Esportazione statistiche: {ex.Message}", LogLevel.Error);
MessageBox.Show($"Errore: {ex.Message}", "Errore Esportazione", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
// New handler for Settings tab
private void SaveCookieButton_Click(object sender, RoutedEventArgs e)
{
try
{
var cookieValue = SettingsCookieTextBox.Text?.Trim();
if (string.IsNullOrWhiteSpace(cookieValue))
{
MessageBox.Show("Inserisci un valore valido per il cookie.", "Errore", MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
// Initialize session with the cookie
var cookieString = $"__stattrb={cookieValue}";
_auctionMonitor.InitializeSessionWithCookie(cookieString, "");
StartButton.IsEnabled = true;
// Save session securely
var session = _auctionMonitor.GetSession();
SessionManager.SaveSession(session);
Log("Sessione configurata da impostazioni");
Log("Cookie salvato in modo sicuro");
// Update user info
Task.Run(async () =>
{
var userData = await _auctionMonitor.GetUserDataAsync();
if (userData != null)
{
Dispatcher.Invoke(() =>
{
SetUserBanner(userData.Username, userData.RemainingBids);
Log($"[OK] Utente: {userData.Username}, Puntate residue: {userData.RemainingBids}");
});
}
});
MessageBox.Show("Cookie salvato con successo!", "Salva Cookie", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
Log($"[ERRORE] Salvataggio cookie: {ex.Message}", LogLevel.Error);
MessageBox.Show($"Errore: {ex.Message}", "Errore", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
// Add empty handlers required by XAML (no-op to avoid forcing runtime width changes)
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// Intentionally left empty to allow designer sizing to take precedence at runtime.
}
private void ToggleTabsButton_Checked(object sender, RoutedEventArgs e)
{
// No runtime width adjustments to avoid layout mismatch with designer.
}
private void ToggleTabsButton_Unchecked(object sender, RoutedEventArgs e)
{
// No runtime width adjustments to avoid layout mismatch with designer.
}
} }
} }

View File

@@ -0,0 +1,46 @@
using System;
using System.IO;
using System.Text.Json;
namespace AutoBidder.Utilities
{
internal class AppSettings
{
public string? ExportPath { get; set; }
public string? LastExportExt { get; set; }
public string ExportScope { get; set; } = "All"; // All, Closed, Unknown
public bool IncludeOnlyUsedBids { get; set; } = true;
public bool IncludeLogs { get; set; } = false;
public bool IncludeUserBids { get; set; } = false;
}
internal static class SettingsManager
{
private static readonly string _folder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "AutoBidder");
private static readonly string _file = Path.Combine(_folder, "settings.json");
public static AppSettings Load()
{
try
{
if (!File.Exists(_file)) return new AppSettings();
var txt = File.ReadAllText(_file);
var s = JsonSerializer.Deserialize<AppSettings>(txt);
if (s == null) return new AppSettings();
return s;
}
catch { return new AppSettings(); }
}
public static void Save(AppSettings settings)
{
try
{
if (!Directory.Exists(_folder)) Directory.CreateDirectory(_folder);
var txt = JsonSerializer.Serialize(settings, new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText(_file, txt);
}
catch { }
}
}
}