Added checking for updates

This commit is contained in:
erik 2025-05-31 00:06:18 +02:00
parent cc15863e81
commit 5ec6525257
19 changed files with 1231 additions and 100 deletions

364
ViewModels/MainViewModel.cs Normal file
View file

@ -0,0 +1,364 @@
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();
}
}
}