Files
Mimante/Mimante/Controls/FreeBidsControl.xaml.cs
T
Alby96 7ca504a70a 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.
2026-08-04 21:49:53 +02:00

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); }
}
}
}