- 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.
353 lines
16 KiB
C#
353 lines
16 KiB
C#
using System;
|
|
using System.Collections.ObjectModel;
|
|
using System.Windows;
|
|
using System.Windows.Controls;
|
|
using AutoBidder.Utilities;
|
|
|
|
namespace AutoBidder.Controls
|
|
{
|
|
/// <summary>
|
|
/// Scheda Puntate: stato del riscatto automatico, contatori e registro.
|
|
///
|
|
/// <para>Come gli altri pannelli non conosce né la rete né gli archivi: mostra ciò che
|
|
/// gli viene passato e segnala le richieste dell'utente con eventi.</para>
|
|
/// </summary>
|
|
public partial class FreeBidsControl : UserControl
|
|
{
|
|
/// <summary>Una riga del registro dei riscatti.</summary>
|
|
public sealed record ActivityEntry(string Time, string Message, bool IsProblem);
|
|
|
|
/// <summary>
|
|
/// Il registro è volutamente limitato e solo in memoria: serve a capire cosa è
|
|
/// successo mentre si guardava altrove, non a essere uno storico — quello sono i
|
|
/// contatori, che invece durano.
|
|
/// </summary>
|
|
private const int MaxActivityEntries = 200;
|
|
|
|
private readonly ObservableCollection<ActivityEntry> _activity = new();
|
|
|
|
/// <summary>Evita che riempire i campi dalle impostazioni scateni un salvataggio.</summary>
|
|
private bool _loading;
|
|
|
|
public FreeBidsControl()
|
|
{
|
|
InitializeComponent();
|
|
ActivityList.ItemsSource = _activity;
|
|
}
|
|
|
|
// ── Aggiornamento dello stato ────────────────────────────────────
|
|
|
|
public void SetBalance(int? bids) =>
|
|
BalanceText.Text = bids.HasValue
|
|
? $"{bids.Value} puntate sul conto"
|
|
: "saldo non disponibile";
|
|
|
|
/// <summary>Allinea i campi alle impostazioni salvate senza scatenare eventi.</summary>
|
|
public void SetOptions(bool autoClaimEnabled, int checkMinutes)
|
|
{
|
|
_loading = true;
|
|
try
|
|
{
|
|
AutoClaimCheckBox.IsChecked = autoClaimEnabled;
|
|
CheckMinutesTextBox.Text = checkMinutes.ToString();
|
|
}
|
|
finally { _loading = false; }
|
|
}
|
|
|
|
public bool AutoClaimEnabled => AutoClaimCheckBox.IsChecked == true;
|
|
|
|
/// <summary>Codice o collegamento digitato per il riscatto manuale.</summary>
|
|
public string PromoCode => PromoCodeTextBox.Text?.Trim() ?? "";
|
|
|
|
/// <summary>Svuota il campo dopo un riscatto andato a buon fine.</summary>
|
|
public void ClearPromoCode() => PromoCodeTextBox.Text = "";
|
|
|
|
/// <summary>
|
|
/// Riga di stato della raccolta: da dove legge, quanti codici sono già stati presi e
|
|
/// com'è andato l'ultimo giro.
|
|
/// </summary>
|
|
public void SetHarvestStatus(string sourceUrl, int claimedCodes, string? lastOutcome)
|
|
{
|
|
var known = claimedCodes == 1
|
|
? "1 codice già preso"
|
|
: $"{claimedCodes} codici già presi";
|
|
|
|
HarvestStatusText.Text = lastOutcome == null
|
|
? $"Sorgente: {sourceUrl} · {known}."
|
|
: $"Sorgente: {sourceUrl} · {known} · ultimo giro: {lastOutcome}";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Mostra dove sta il file dei riferimenti, ed eventualmente perché non è stato
|
|
/// possibile usarlo: sono i valori che decidono ogni chiamata di riscatto, e sapere
|
|
/// che se ne stanno usando altri è la differenza fra correggere il file e cercare un
|
|
/// guasto che non c'è.
|
|
/// </summary>
|
|
public void SetConfigPath(string path, string? problem)
|
|
{
|
|
ConfigPathText.Text = problem == null
|
|
? $"Indirizzi, intestazioni e parametri del riscatto: {path}"
|
|
: $"{problem} — {path}";
|
|
|
|
ConfigPathText.SetResourceReference(TextBlock.ForegroundProperty,
|
|
problem == null ? "Brush.TextMuted" : "Brush.Warning");
|
|
}
|
|
|
|
/// <summary>Minuti richiesti, già riportati entro il minimo consentito.</summary>
|
|
public int CheckMinutes =>
|
|
int.TryParse(CheckMinutesTextBox.Text?.Trim(), out var minutes) ? Math.Max(5, minutes) : 30;
|
|
|
|
/// <summary>Riga di stato: acceso o spento, e quando tocca al prossimo giro.</summary>
|
|
public void SetSchedule(bool running, DateTime? lastCheck, DateTime? nextCheck)
|
|
{
|
|
StatusPillText.Text = running ? "riscatto attivo" : "riscatto spento";
|
|
StatusPillText.SetResourceReference(TextBlock.ForegroundProperty,
|
|
running ? "Brush.Success" : "Brush.TextMuted");
|
|
|
|
if (!running)
|
|
{
|
|
ScheduleText.Text = "Riscatto automatico spento: puoi comunque usare «Controlla adesso».";
|
|
return;
|
|
}
|
|
|
|
var last = lastCheck.HasValue
|
|
? $"ultimo controllo alle {lastCheck.Value:HH:mm}"
|
|
: "nessun controllo ancora eseguito";
|
|
|
|
var next = nextCheck.HasValue
|
|
? $", il prossimo alle {nextCheck.Value:HH:mm}"
|
|
: "";
|
|
|
|
ScheduleText.Text = char.ToUpper(last[0]) + last[1..] + next + ".";
|
|
}
|
|
|
|
/// <summary>Ridisegna i contatori.</summary>
|
|
public void SetCounters(FreeBidsStats.Snapshot s)
|
|
{
|
|
SessionBidsText.Text = s.SessionBids.ToString();
|
|
TodayBidsText.Text = s.BidsToday.ToString();
|
|
TotalBidsText.Text = s.TotalBids.ToString();
|
|
TotalClaimedText.Text = s.TotalClaimed.ToString();
|
|
|
|
var lastClaim = s.LastClaimAt.HasValue
|
|
? $"ultimo riscatto il {s.LastClaimAt.Value:dd/MM/yyyy} alle {s.LastClaimAt.Value:HH:mm}"
|
|
: "nessun riscatto ancora andato a buon fine";
|
|
|
|
CountersDetailText.Text =
|
|
$"{s.SessionChecks} controlli in questa sessione · {s.TotalChecks} da sempre · " +
|
|
$"{s.SessionClaimed} ricompense prese in sessione · {lastClaim}.";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Aggiunge una riga al registro, a video e su file.
|
|
///
|
|
/// <para>Il file serve perché questo pannello è l'unico posto in cui si vede cosa ha
|
|
/// fatto il riscatto automatico, e il riscatto lavora quando nessuno guarda: senza
|
|
/// registro su disco, tutto ciò che è successo mentre l'applicazione era chiusa —
|
|
/// o nelle duecento righe precedenti — sarebbe perso.</para>
|
|
/// </summary>
|
|
public void AppendActivity(string message, bool isProblem = false)
|
|
{
|
|
TextLogService.FreeBids(message, isProblem);
|
|
|
|
_activity.Insert(0, new ActivityEntry(DateTime.Now.ToString("HH:mm:ss"), message, isProblem));
|
|
|
|
while (_activity.Count > MaxActivityEntries)
|
|
_activity.RemoveAt(_activity.Count - 1);
|
|
|
|
ActivityEmptyText.Visibility = Visibility.Collapsed;
|
|
}
|
|
|
|
// ── Eventi dell'interfaccia ──────────────────────────────────────
|
|
|
|
private void RefreshButton_Click(object sender, RoutedEventArgs e)
|
|
=> RaiseEvent(new RoutedEventArgs(RefreshBalanceClickedEvent, this));
|
|
|
|
private void CheckNowButton_Click(object sender, RoutedEventArgs e)
|
|
=> RaiseEvent(new RoutedEventArgs(CheckNowClickedEvent, this));
|
|
|
|
private void OpenExternalButton_Click(object sender, RoutedEventArgs e)
|
|
=> RaiseEvent(new RoutedEventArgs(OpenExternalClickedEvent, this));
|
|
|
|
private void OpenInternalButton_Click(object sender, RoutedEventArgs e)
|
|
=> RaiseEvent(new RoutedEventArgs(OpenInternalClickedEvent, this));
|
|
|
|
private void OpenVouchersButton_Click(object sender, RoutedEventArgs e)
|
|
=> RaiseEvent(new RoutedEventArgs(OpenVouchersClickedEvent, this));
|
|
|
|
private void OpenBuyBidsButton_Click(object sender, RoutedEventArgs e)
|
|
=> RaiseEvent(new RoutedEventArgs(OpenBuyBidsClickedEvent, this));
|
|
|
|
private void ResetCountersButton_Click(object sender, RoutedEventArgs e)
|
|
=> RaiseEvent(new RoutedEventArgs(ResetCountersClickedEvent, this));
|
|
|
|
private void AutoClaimCheckBox_Changed(object sender, RoutedEventArgs e)
|
|
{
|
|
if (_loading) return;
|
|
RaiseEvent(new RoutedEventArgs(OptionsChangedEvent, this));
|
|
}
|
|
|
|
private void CheckMinutesTextBox_LostFocus(object sender, RoutedEventArgs e)
|
|
{
|
|
if (_loading) return;
|
|
|
|
// Si riscrive il valore corretto: chi ha digitato "2" deve vedere che il
|
|
// minimo applicato è 5, invece di credere di aver impostato due minuti.
|
|
var minutes = CheckMinutes;
|
|
if (CheckMinutesTextBox.Text != minutes.ToString())
|
|
CheckMinutesTextBox.Text = minutes.ToString();
|
|
|
|
RaiseEvent(new RoutedEventArgs(OptionsChangedEvent, this));
|
|
}
|
|
|
|
private void HarvestButton_Click(object sender, RoutedEventArgs e)
|
|
=> RaiseEvent(new RoutedEventArgs(HarvestClickedEvent, this));
|
|
|
|
private void OpenSourceButton_Click(object sender, RoutedEventArgs e)
|
|
=> RaiseEvent(new RoutedEventArgs(OpenPromoSourceClickedEvent, this));
|
|
|
|
private void ForgetPromosButton_Click(object sender, RoutedEventArgs e)
|
|
=> RaiseEvent(new RoutedEventArgs(ForgetPromosClickedEvent, this));
|
|
|
|
private void ClaimPromoButton_Click(object sender, RoutedEventArgs e)
|
|
=> RaiseEvent(new RoutedEventArgs(ClaimPromoClickedEvent, this));
|
|
|
|
/// <summary>Invio nel campo vale come clic: si incolla e si preme invio.</summary>
|
|
private void PromoCodeTextBox_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
|
|
{
|
|
if (e.Key != System.Windows.Input.Key.Enter) return;
|
|
|
|
e.Handled = true;
|
|
RaiseEvent(new RoutedEventArgs(ClaimPromoClickedEvent, this));
|
|
}
|
|
|
|
private void OpenConfigButton_Click(object sender, RoutedEventArgs e)
|
|
=> RaiseEvent(new RoutedEventArgs(OpenConfigClickedEvent, this));
|
|
|
|
private void ClearLogButton_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
_activity.Clear();
|
|
ActivityEmptyText.Visibility = Visibility.Visible;
|
|
}
|
|
|
|
// ── Routed events ────────────────────────────────────────────────
|
|
|
|
public static readonly RoutedEvent RefreshBalanceClickedEvent = EventManager.RegisterRoutedEvent(
|
|
"RefreshBalanceClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(FreeBidsControl));
|
|
|
|
public static readonly RoutedEvent CheckNowClickedEvent = EventManager.RegisterRoutedEvent(
|
|
"CheckNowClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(FreeBidsControl));
|
|
|
|
public static readonly RoutedEvent OpenExternalClickedEvent = EventManager.RegisterRoutedEvent(
|
|
"OpenExternalClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(FreeBidsControl));
|
|
|
|
public static readonly RoutedEvent OpenInternalClickedEvent = EventManager.RegisterRoutedEvent(
|
|
"OpenInternalClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(FreeBidsControl));
|
|
|
|
public static readonly RoutedEvent OpenVouchersClickedEvent = EventManager.RegisterRoutedEvent(
|
|
"OpenVouchersClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(FreeBidsControl));
|
|
|
|
public static readonly RoutedEvent OpenBuyBidsClickedEvent = EventManager.RegisterRoutedEvent(
|
|
"OpenBuyBidsClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(FreeBidsControl));
|
|
|
|
public static readonly RoutedEvent ResetCountersClickedEvent = EventManager.RegisterRoutedEvent(
|
|
"ResetCountersClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(FreeBidsControl));
|
|
|
|
public static readonly RoutedEvent OptionsChangedEvent = EventManager.RegisterRoutedEvent(
|
|
"OptionsChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(FreeBidsControl));
|
|
|
|
public static readonly RoutedEvent ClaimPromoClickedEvent = EventManager.RegisterRoutedEvent(
|
|
"ClaimPromoClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(FreeBidsControl));
|
|
|
|
public static readonly RoutedEvent HarvestClickedEvent = EventManager.RegisterRoutedEvent(
|
|
"HarvestClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(FreeBidsControl));
|
|
|
|
public static readonly RoutedEvent OpenPromoSourceClickedEvent = EventManager.RegisterRoutedEvent(
|
|
"OpenPromoSourceClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(FreeBidsControl));
|
|
|
|
public static readonly RoutedEvent ForgetPromosClickedEvent = EventManager.RegisterRoutedEvent(
|
|
"ForgetPromosClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(FreeBidsControl));
|
|
|
|
public static readonly RoutedEvent OpenConfigClickedEvent = EventManager.RegisterRoutedEvent(
|
|
"OpenConfigClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(FreeBidsControl));
|
|
|
|
public event RoutedEventHandler RefreshBalanceClicked
|
|
{
|
|
add { AddHandler(RefreshBalanceClickedEvent, value); }
|
|
remove { RemoveHandler(RefreshBalanceClickedEvent, value); }
|
|
}
|
|
|
|
public event RoutedEventHandler CheckNowClicked
|
|
{
|
|
add { AddHandler(CheckNowClickedEvent, value); }
|
|
remove { RemoveHandler(CheckNowClickedEvent, value); }
|
|
}
|
|
|
|
public event RoutedEventHandler OpenExternalClicked
|
|
{
|
|
add { AddHandler(OpenExternalClickedEvent, value); }
|
|
remove { RemoveHandler(OpenExternalClickedEvent, value); }
|
|
}
|
|
|
|
public event RoutedEventHandler OpenInternalClicked
|
|
{
|
|
add { AddHandler(OpenInternalClickedEvent, value); }
|
|
remove { RemoveHandler(OpenInternalClickedEvent, value); }
|
|
}
|
|
|
|
public event RoutedEventHandler OpenVouchersClicked
|
|
{
|
|
add { AddHandler(OpenVouchersClickedEvent, value); }
|
|
remove { RemoveHandler(OpenVouchersClickedEvent, value); }
|
|
}
|
|
|
|
public event RoutedEventHandler OpenBuyBidsClicked
|
|
{
|
|
add { AddHandler(OpenBuyBidsClickedEvent, value); }
|
|
remove { RemoveHandler(OpenBuyBidsClickedEvent, value); }
|
|
}
|
|
|
|
public event RoutedEventHandler ResetCountersClicked
|
|
{
|
|
add { AddHandler(ResetCountersClickedEvent, value); }
|
|
remove { RemoveHandler(ResetCountersClickedEvent, value); }
|
|
}
|
|
|
|
public event RoutedEventHandler OptionsChanged
|
|
{
|
|
add { AddHandler(OptionsChangedEvent, value); }
|
|
remove { RemoveHandler(OptionsChangedEvent, value); }
|
|
}
|
|
|
|
public event RoutedEventHandler ClaimPromoClicked
|
|
{
|
|
add { AddHandler(ClaimPromoClickedEvent, value); }
|
|
remove { RemoveHandler(ClaimPromoClickedEvent, value); }
|
|
}
|
|
|
|
public event RoutedEventHandler HarvestClicked
|
|
{
|
|
add { AddHandler(HarvestClickedEvent, value); }
|
|
remove { RemoveHandler(HarvestClickedEvent, value); }
|
|
}
|
|
|
|
public event RoutedEventHandler OpenPromoSourceClicked
|
|
{
|
|
add { AddHandler(OpenPromoSourceClickedEvent, value); }
|
|
remove { RemoveHandler(OpenPromoSourceClickedEvent, value); }
|
|
}
|
|
|
|
public event RoutedEventHandler ForgetPromosClicked
|
|
{
|
|
add { AddHandler(ForgetPromosClickedEvent, value); }
|
|
remove { RemoveHandler(ForgetPromosClickedEvent, value); }
|
|
}
|
|
|
|
public event RoutedEventHandler OpenConfigClicked
|
|
{
|
|
add { AddHandler(OpenConfigClickedEvent, value); }
|
|
remove { RemoveHandler(OpenConfigClickedEvent, value); }
|
|
}
|
|
}
|
|
}
|