using ytLive.Helpers;
using ytLive.Services;
namespace ytLive.ViewModels;
public partial class MainViewModel
{
// TASK 10 — Polar license validation
private string _licenseKey = string.Empty;
private string _licenseMessage = string.Empty;
private bool _isLicenseValidating;
private bool _isLicenseEntryOpen;
private static readonly TimeSpan OfflineGracePeriod = TimeSpan.FromDays(14);
/// True when the user holds a valid LlamaCasty license (Polar). When
/// false, the branding flash runs on every live stream.
private bool _isPremium;
public bool IsPremium
{
get => _isPremium;
private set
{
if (!SetProperty(ref _isPremium, value)) return;
// The flash is composited into the broadcast, so the gate has to be pushed
// all the way into the renderer — not just the preview.
_brandFlash.Enabled = !value;
}
}
// TASK 10 — license key entry
public string LicenseKey
{
get => _licenseKey;
set => SetProperty(ref _licenseKey, value);
}
public string LicenseMessage
{
get => _licenseMessage;
private set => SetProperty(ref _licenseMessage, value);
}
public bool IsLicenseValidating
{
get => _isLicenseValidating;
private set => SetProperty(ref _isLicenseValidating, value);
}
public bool IsLicenseEntryOpen
{
get => _isLicenseEntryOpen;
set
{
if (SetProperty(ref _isLicenseEntryOpen, value))
OnPropertyChanged(nameof(IsHubVisible));
}
}
/// The About hub is visible only when neither the licenses panel
/// nor the license entry panel is open.
public bool IsHubVisible => !IsLicensesOpen && !IsLicenseEntryOpen;
// ─── TASK 10 — Polar license validation ───
/// At startup, load the cached license state and re-validate online
/// if a key exists. If offline and within the 14-day grace period, the cached
/// premium state is trusted. If the grace period is exceeded, premium lapses.
private async Task ValidateLicenseAtStartupAsync()
{
var savedKey = _layoutStore.LoadLicenseKey();
if (string.IsNullOrWhiteSpace(savedKey))
{
IsPremium = false;
return;
}
LicenseKey = savedKey;
var validatedAt = _layoutStore.LoadLicenseValidatedAt();
var cachedPremium = _layoutStore.LoadIsPremium();
// If the last validation was within the grace period, trust the cache
// while we attempt an online re-validation in the background.
if (validatedAt.HasValue && DateTime.UtcNow - validatedAt.Value < OfflineGracePeriod)
{
IsPremium = cachedPremium;
}
// Try an online check — if it succeeds, it overrides the cache.
try
{
var service = new PolarLicenseService();
var result = await service.ValidateAsync(savedKey);
if (result.IsValid)
{
IsPremium = true;
_layoutStore.SaveIsPremium(true);
_layoutStore.SaveLicenseValidatedAt(DateTime.UtcNow);
}
else
{
// Key revoked or expired — clear premium.
IsPremium = false;
_layoutStore.SaveIsPremium(false);
_layoutStore.SaveLicenseValidatedAt(DateTime.UtcNow);
AppLog.Write($"License validation failed: {result.Error}");
_notifications.Warning("Premium is no longer active",
$"{result.Error} — watermark branding returns until the license is renewed.");
}
}
catch (Exception ex)
{
// Offline or network error — keep the cached state, log it.
AppLog.Write($"License re-validation skipped (offline?): {ex.Message}");
if (!validatedAt.HasValue || DateTime.UtcNow - validatedAt.Value >= OfflineGracePeriod)
{
IsPremium = false;
_layoutStore.SaveIsPremium(false);
}
}
}
/// Called from the Activate button in the About overlay. Validates
/// the entered key, persists it, and flips premium state.
private async Task ActivateLicenseAsync()
{
if (string.IsNullOrWhiteSpace(LicenseKey)) return;
IsLicenseValidating = true;
LicenseMessage = "Validating\u2026";
try
{
var service = new PolarLicenseService();
var result = await service.ValidateAsync(LicenseKey);
if (result.IsValid)
{
IsPremium = true;
_layoutStore.SaveLicenseKey(LicenseKey.Trim());
_layoutStore.SaveIsPremium(true);
_layoutStore.SaveLicenseValidatedAt(DateTime.UtcNow);
LicenseMessage = "Premium activated! The watermark has been removed.";
AppLog.Write("License activated successfully");
}
else
{
IsPremium = false;
_layoutStore.SaveLicenseKey(null);
_layoutStore.SaveIsPremium(false);
LicenseMessage = result.Error ?? "Invalid license key.";
AppLog.Write($"License activation failed: {result.Error}");
}
}
catch (Exception ex)
{
LicenseMessage = $"Activation error: {ex.Message}";
AppLog.Write($"License activation error: {ex}");
}
finally
{
IsLicenseValidating = false;
}
}
}