364 lines
No EOL
12 KiB
C#
364 lines
No EOL
12 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Windows.Input;
|
|
using MossyUpdater.Services;
|
|
using Ookii.Dialogs.Wpf;
|
|
|
|
namespace MossyUpdater.ViewModels
|
|
{
|
|
public class MainViewModel : ViewModelBase, IDisposable
|
|
{
|
|
private readonly IDownloadService _downloadService;
|
|
private readonly ISettingsService _settingsService;
|
|
private readonly ILogger _logger;
|
|
private readonly IQuotesService _quotesService;
|
|
private CancellationTokenSource? _cancellationTokenSource;
|
|
private System.Windows.Threading.DispatcherTimer? _quoteTimer;
|
|
|
|
private string _url = "https://git.snakedesert.se/SawatoMosswartsEnjoyersClub/MosswartMassacre/raw/branch/spawn-detection/MosswartMassacre/bin/Release/MosswartMassacre.dll";
|
|
private string _folder = string.Empty;
|
|
private string _filename = "MosswartMassacre.dll";
|
|
private string _statusMessage = string.Empty;
|
|
private System.Windows.Media.Brush _statusColor = System.Windows.Media.Brushes.Green;
|
|
private double _progressValue = 0;
|
|
private bool _isProgressVisible = false;
|
|
private bool _isDownloading = false;
|
|
private string _updateStatus = string.Empty;
|
|
private bool _updateAvailable = false;
|
|
private string _currentQuote = string.Empty;
|
|
private double _quoteOpacity = 1.0;
|
|
|
|
public MainViewModel(IDownloadService downloadService, ISettingsService settingsService, ILogger logger, IQuotesService quotesService)
|
|
{
|
|
_downloadService = downloadService ?? throw new ArgumentNullException(nameof(downloadService));
|
|
_settingsService = settingsService ?? throw new ArgumentNullException(nameof(settingsService));
|
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
|
_quotesService = quotesService ?? throw new ArgumentNullException(nameof(quotesService));
|
|
|
|
BrowseCommand = new RelayCommand(BrowseFolder);
|
|
DownloadCommand = new AsyncRelayCommand(DownloadAndUnblock, CanDownload);
|
|
CancelCommand = new RelayCommand(CancelDownload, () => IsDownloading);
|
|
CheckUpdatesCommand = new AsyncRelayCommand(CheckForUpdates);
|
|
|
|
InitializeQuoteRotation();
|
|
_ = LoadSettingsAsync();
|
|
}
|
|
|
|
public string Url
|
|
{
|
|
get => _url;
|
|
set => SetProperty(ref _url, value);
|
|
}
|
|
|
|
public string Folder
|
|
{
|
|
get => _folder;
|
|
set => SetProperty(ref _folder, value);
|
|
}
|
|
|
|
public string Filename
|
|
{
|
|
get => _filename;
|
|
set => SetProperty(ref _filename, value);
|
|
}
|
|
|
|
public string StatusMessage
|
|
{
|
|
get => _statusMessage;
|
|
set => SetProperty(ref _statusMessage, value);
|
|
}
|
|
|
|
public System.Windows.Media.Brush StatusColor
|
|
{
|
|
get => _statusColor;
|
|
set => SetProperty(ref _statusColor, value);
|
|
}
|
|
|
|
public double ProgressValue
|
|
{
|
|
get => _progressValue;
|
|
set => SetProperty(ref _progressValue, value);
|
|
}
|
|
|
|
public bool IsProgressVisible
|
|
{
|
|
get => _isProgressVisible;
|
|
set => SetProperty(ref _isProgressVisible, value);
|
|
}
|
|
|
|
public bool IsDownloading
|
|
{
|
|
get => _isDownloading;
|
|
set
|
|
{
|
|
SetProperty(ref _isDownloading, value);
|
|
OnPropertyChanged(nameof(CanInteract));
|
|
}
|
|
}
|
|
|
|
public string UpdateStatus
|
|
{
|
|
get => _updateStatus;
|
|
set => SetProperty(ref _updateStatus, value);
|
|
}
|
|
|
|
public bool UpdateAvailable
|
|
{
|
|
get => _updateAvailable;
|
|
set => SetProperty(ref _updateAvailable, value);
|
|
}
|
|
|
|
public string CurrentQuote
|
|
{
|
|
get => _currentQuote;
|
|
set => SetProperty(ref _currentQuote, value);
|
|
}
|
|
|
|
public double QuoteOpacity
|
|
{
|
|
get => _quoteOpacity;
|
|
set => SetProperty(ref _quoteOpacity, value);
|
|
}
|
|
|
|
public bool CanInteract => !IsDownloading;
|
|
|
|
public ICommand BrowseCommand { get; }
|
|
public ICommand DownloadCommand { get; }
|
|
public ICommand CancelCommand { get; }
|
|
public ICommand CheckUpdatesCommand { get; }
|
|
|
|
private void BrowseFolder()
|
|
{
|
|
var dialog = new VistaFolderBrowserDialog
|
|
{
|
|
Description = "Select a destination folder",
|
|
UseDescriptionForTitle = true,
|
|
SelectedPath = Folder
|
|
};
|
|
|
|
if (dialog.ShowDialog() == true)
|
|
{
|
|
Folder = dialog.SelectedPath;
|
|
_ = SaveSettingsAsync();
|
|
}
|
|
}
|
|
|
|
private bool CanDownload()
|
|
{
|
|
return !IsDownloading &&
|
|
!string.IsNullOrWhiteSpace(Url) &&
|
|
!string.IsNullOrWhiteSpace(Folder) &&
|
|
!string.IsNullOrWhiteSpace(Filename);
|
|
}
|
|
|
|
private async Task DownloadAndUnblock()
|
|
{
|
|
if (string.IsNullOrWhiteSpace(Url) || string.IsNullOrWhiteSpace(Folder) || string.IsNullOrWhiteSpace(Filename))
|
|
{
|
|
SetStatus("Please fill in all fields.", System.Windows.Media.Brushes.Red);
|
|
return;
|
|
}
|
|
|
|
var fullPath = Path.Combine(Folder, Filename);
|
|
_cancellationTokenSource = new CancellationTokenSource();
|
|
|
|
try
|
|
{
|
|
IsDownloading = true;
|
|
IsProgressVisible = true;
|
|
ProgressValue = 0;
|
|
|
|
var progress = new Progress<DownloadProgress>(OnDownloadProgress);
|
|
var result = await _downloadService.DownloadFileAsync(Url, fullPath, progress, _cancellationTokenSource.Token);
|
|
|
|
if (result.Success)
|
|
{
|
|
_downloadService.UnblockFile(fullPath);
|
|
SetStatus($"Downloaded and unblocked successfully!\n{fullPath}\nSize: {FormatBytes(result.BytesDownloaded)}, Duration: {result.Duration:mm\\:ss}", System.Windows.Media.Brushes.Green);
|
|
ProgressValue = 100;
|
|
|
|
await CheckForUpdates();
|
|
}
|
|
else if (!string.IsNullOrEmpty(result.ErrorMessage))
|
|
{
|
|
SetStatus($"Download failed: {result.ErrorMessage}", System.Windows.Media.Brushes.Red);
|
|
ProgressValue = 0;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
SetStatus($"Unexpected error: {ex.Message}", System.Windows.Media.Brushes.Red);
|
|
ProgressValue = 0;
|
|
}
|
|
finally
|
|
{
|
|
IsDownloading = false;
|
|
_cancellationTokenSource?.Dispose();
|
|
_cancellationTokenSource = null;
|
|
|
|
await Task.Delay(3000);
|
|
IsProgressVisible = false;
|
|
}
|
|
}
|
|
|
|
private void CancelDownload()
|
|
{
|
|
_cancellationTokenSource?.Cancel();
|
|
SetStatus("Download cancelled by user.", System.Windows.Media.Brushes.Orange);
|
|
}
|
|
|
|
private async Task CheckForUpdates()
|
|
{
|
|
if (string.IsNullOrWhiteSpace(Url) || string.IsNullOrWhiteSpace(Folder) || string.IsNullOrWhiteSpace(Filename))
|
|
{
|
|
UpdateStatus = "";
|
|
UpdateAvailable = false;
|
|
return;
|
|
}
|
|
|
|
var localPath = Path.Combine(Folder, Filename);
|
|
|
|
try
|
|
{
|
|
var updateInfo = await _downloadService.CheckForUpdatesAsync(Url, localPath);
|
|
|
|
if (!string.IsNullOrEmpty(updateInfo.ErrorMessage))
|
|
{
|
|
UpdateStatus = $"Update check failed: {updateInfo.ErrorMessage}";
|
|
UpdateAvailable = false;
|
|
}
|
|
else if (updateInfo.UpdateAvailable)
|
|
{
|
|
UpdateStatus = "Update available! Remote file is newer or different.";
|
|
UpdateAvailable = true;
|
|
}
|
|
else
|
|
{
|
|
UpdateStatus = "File is up to date.";
|
|
UpdateAvailable = false;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
UpdateStatus = $"Update check error: {ex.Message}";
|
|
UpdateAvailable = false;
|
|
}
|
|
}
|
|
|
|
private void OnDownloadProgress(DownloadProgress progress)
|
|
{
|
|
ProgressValue = progress.ProgressPercentage;
|
|
SetStatus(progress.Status, System.Windows.Media.Brushes.Blue);
|
|
}
|
|
|
|
private void SetStatus(string message, System.Windows.Media.Brush color)
|
|
{
|
|
StatusMessage = message;
|
|
StatusColor = color;
|
|
}
|
|
|
|
private async Task LoadSettingsAsync()
|
|
{
|
|
try
|
|
{
|
|
var settings = await _settingsService.LoadSettingsAsync();
|
|
Url = settings.DefaultUrl;
|
|
Folder = settings.LastUsedFolder;
|
|
Filename = settings.DefaultFilename;
|
|
|
|
if (settings.CheckForUpdatesOnStartup && !string.IsNullOrWhiteSpace(Url) && !string.IsNullOrWhiteSpace(Folder) && !string.IsNullOrWhiteSpace(Filename))
|
|
{
|
|
await CheckForUpdates();
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await _logger.LogErrorAsync("Failed to load settings", ex);
|
|
}
|
|
}
|
|
|
|
private async Task SaveSettingsAsync()
|
|
{
|
|
try
|
|
{
|
|
var settings = new AppSettings
|
|
{
|
|
DefaultUrl = Url,
|
|
LastUsedFolder = Folder,
|
|
DefaultFilename = Filename,
|
|
CheckForUpdatesOnStartup = true,
|
|
DownloadTimeoutMinutes = 10
|
|
};
|
|
|
|
await _settingsService.SaveSettingsAsync(settings);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await _logger.LogErrorAsync("Failed to save settings", ex);
|
|
}
|
|
}
|
|
|
|
private static string FormatBytes(long bytes)
|
|
{
|
|
string[] suffixes = { "B", "KB", "MB", "GB", "TB" };
|
|
int counter = 0;
|
|
decimal number = bytes;
|
|
|
|
while (Math.Round(number / 1024) >= 1)
|
|
{
|
|
number /= 1024;
|
|
counter++;
|
|
}
|
|
|
|
return $"{number:n1} {suffixes[counter]}";
|
|
}
|
|
|
|
private void InitializeQuoteRotation()
|
|
{
|
|
CurrentQuote = _quotesService.GetNextQuote();
|
|
|
|
_quoteTimer = new System.Windows.Threading.DispatcherTimer
|
|
{
|
|
Interval = TimeSpan.FromSeconds(5)
|
|
};
|
|
_quoteTimer.Tick += OnQuoteTimerTick;
|
|
_quoteTimer.Start();
|
|
}
|
|
|
|
private async void OnQuoteTimerTick(object? sender, EventArgs e)
|
|
{
|
|
await RotateQuoteWithFade();
|
|
}
|
|
|
|
private async Task RotateQuoteWithFade()
|
|
{
|
|
const double fadeOutDuration = 300;
|
|
const double fadeInDuration = 300;
|
|
const int fadeSteps = 30;
|
|
|
|
var fadeOutDelay = TimeSpan.FromMilliseconds(fadeOutDuration / fadeSteps);
|
|
var fadeInDelay = TimeSpan.FromMilliseconds(fadeInDuration / fadeSteps);
|
|
|
|
for (int i = fadeSteps; i >= 0; i--)
|
|
{
|
|
QuoteOpacity = (double)i / fadeSteps;
|
|
await Task.Delay(fadeOutDelay);
|
|
}
|
|
|
|
CurrentQuote = _quotesService.GetNextQuote();
|
|
|
|
for (int i = 0; i <= fadeSteps; i++)
|
|
{
|
|
QuoteOpacity = (double)i / fadeSteps;
|
|
await Task.Delay(fadeInDelay);
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_quoteTimer?.Stop();
|
|
_quoteTimer = null;
|
|
_cancellationTokenSource?.Dispose();
|
|
}
|
|
}
|
|
} |