diff --git a/ZuneModdingHelper/Controls/ZuneLightDialog.xaml b/ZuneModdingHelper/Controls/ZuneLightDialog.xaml
index 12410d7..febd16a 100644
--- a/ZuneModdingHelper/Controls/ZuneLightDialog.xaml
+++ b/ZuneModdingHelper/Controls/ZuneLightDialog.xaml
@@ -41,7 +41,8 @@
-
diff --git a/ZuneModdingHelper/Pages/AboutPage.xaml b/ZuneModdingHelper/Pages/AboutPage.xaml
index a5175b6..823833e 100644
--- a/ZuneModdingHelper/Pages/AboutPage.xaml
+++ b/ZuneModdingHelper/Pages/AboutPage.xaml
@@ -38,6 +38,7 @@
diff --git a/ZuneModdingHelper/Pages/AboutPage.xaml.cs b/ZuneModdingHelper/Pages/AboutPage.xaml.cs
index ff64ab7..9358af0 100644
--- a/ZuneModdingHelper/Pages/AboutPage.xaml.cs
+++ b/ZuneModdingHelper/Pages/AboutPage.xaml.cs
@@ -1,16 +1,13 @@
using CommunityToolkit.Mvvm.DependencyInjection;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
-using Octokit;
using Syroot.Windows.IO;
using System;
-using System.IO;
-using System.Linq;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Navigation;
-using ZuneModCore;
using ZuneModdingHelper.Messages;
+using ZuneModdingHelper.Services;
namespace ZuneModdingHelper.Pages
{
@@ -21,15 +18,18 @@ namespace ZuneModdingHelper.Pages
{
const string UPDATES_DIALOG_TITLE = "UPDATES";
- private readonly IGitHubClient? _gitHub = Ioc.Default.GetService();
- private ReleaseAsset _latestAsset;
+ private readonly IUpdateService? _updateService = Ioc.Default.GetService();
+ private UpdateAvailableInfo _updateInfo;
private string _downloadedUpdatePath;
public AboutPage()
{
+ DataContext = this;
InitializeComponent();
}
+ public bool EnableCheckForUpdates => _updateService is not null;
+
private void Link_RequestNavigate(object sender, RequestNavigateEventArgs e)
{
App.OpenInBrowser(e.Uri.AbsoluteUri);
@@ -38,7 +38,7 @@ namespace ZuneModdingHelper.Pages
private async void UpdateCheckButton_Click(object sender, System.Windows.RoutedEventArgs e)
{
- if (_gitHub is null)
+ if (_updateService is null)
return;
WeakReferenceMessenger.Default.Send(new ShowDialogMessage(new ProgressDialogViewModel
@@ -51,16 +51,8 @@ namespace ZuneModdingHelper.Pages
try
{
- // Get releases list from GitHub
- // Why not use the `releases/latest` endpoint? Good question: https://github.com/octokit/octokit.net/issues/2916
- var releases = await _gitHub.Repository.Release.GetAll("ZuneDev", "ZuneModdingHelper");
- var latest = releases
- .Select(r => new { Release = r, Version = ReleaseVersion.Parse(r.TagName) })
- .OrderByDescending(t => t.Version)
- .ThenBy(t => t.Release.Prerelease)
- .First();
-
- if (ReleaseVersion.TryParse(latest.Release.TagName, out var latestVer) || App.Version >= latestVer)
+ _updateInfo = await _updateService.FetchAvailableUpdate();
+ if (_updateInfo is null)
{
WeakReferenceMessenger.Default.Send();
WeakReferenceMessenger.Default.Send(new ShowDialogMessage(new DialogViewModel
@@ -73,13 +65,12 @@ namespace ZuneModdingHelper.Pages
}
// Newer version available, prompt user to download
- _latestAsset = latest.Release.Assets[0];
WeakReferenceMessenger.Default.Send();
WeakReferenceMessenger.Default.Send(new ShowDialogMessage(new DialogViewModel
{
Title = UPDATES_DIALOG_TITLE,
- Description = $"Release {latest.Release.Name} is available. Would you like to download it now?",
+ Description = $"Release {_updateInfo.Name} is available. Would you like to download it now?",
AffirmativeText = "YES",
NegativeText = "NO",
ShowAffirmativeButton = true,
@@ -107,30 +98,14 @@ namespace ZuneModdingHelper.Pages
IsIndeterminate = true,
ShowAffirmativeButton = false,
ShowNegativeButton = false,
- Maximum = 100,
+ Maximum = 1.0,
};
WeakReferenceMessenger.Default.Send(new ShowDialogMessage(prog));
- // Download new version to AppData
- string downloadedFile = Path.Combine(Path.GetTempPath(), _latestAsset.Name);
- if (File.Exists(downloadedFile)) File.Delete(downloadedFile);
- using (var client = new System.Net.WebClient())
- {
- client.DownloadProgressChanged += (object sender, System.Net.DownloadProgressChangedEventArgs e) =>
- {
- // Update UI with download progress
- prog.Progress = e.ProgressPercentage;
- };
-
- prog.Progress = 0;
- prog.IsIndeterminate = false;
- await client.DownloadFileTaskAsync(new Uri(_latestAsset.BrowserDownloadUrl), downloadedFile);
- }
-
// Ask user to save file
Microsoft.Win32.SaveFileDialog saveFileDialog = new()
{
- FileName = _latestAsset.Name,
+ FileName = _updateInfo.Name,
InitialDirectory = new KnownFolder(KnownFolderType.Downloads).Path
};
bool dialogResult = saveFileDialog.ShowDialog() ?? false;
@@ -139,12 +114,17 @@ namespace ZuneModdingHelper.Pages
if (dialogResult)
{
_downloadedUpdatePath = saveFileDialog.FileName;
- File.Copy(downloadedFile, _downloadedUpdatePath, true);
+
+ // Download new version to requested folder with progress updates
+ Progress progress = new(p => prog.Progress = p);
+ prog.Progress = 0;
+ prog.IsIndeterminate = false;
+ await _updateService.DownloadUpdate(_updateInfo, _downloadedUpdatePath, progress);
WeakReferenceMessenger.Default.Send(new ShowDialogMessage(new DialogViewModel
{
Title = UPDATES_DIALOG_TITLE,
- Description = "Update downloaded. Would you like to install the new version?",
+ Description = "Update downloaded. Would you like to launch the new version?",
AffirmativeText = "YES",
NegativeText = "NO",
ShowAffirmativeButton = true,
@@ -152,8 +132,6 @@ namespace ZuneModdingHelper.Pages
OnAction = new AsyncRelayCommand(OnLaunchUpdateDialogResult)
}));
}
-
- File.Delete(downloadedFile);
}
private async Task OnLaunchUpdateDialogResult(bool accepted)
diff --git a/ZuneModdingHelper/Services/GitHubReleasesUpdateService.cs b/ZuneModdingHelper/Services/GitHubReleasesUpdateService.cs
new file mode 100644
index 0000000..20c2ecc
--- /dev/null
+++ b/ZuneModdingHelper/Services/GitHubReleasesUpdateService.cs
@@ -0,0 +1,67 @@
+using Octokit;
+using System;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using ZuneModCore;
+
+namespace ZuneModdingHelper.Services;
+
+public class GitHubReleasesUpdateService(IGitHubClient gitHub) : IUpdateService
+{
+ public async Task FetchAvailableUpdate(CancellationToken token = default)
+ {
+ try
+ {
+ // Get releases list from GitHub
+ // Why not use the `releases/latest` endpoint? Good question: https://github.com/octokit/octokit.net/issues/2916
+ var releases = await gitHub.Repository.Release.GetAll("ZuneDev", "ZuneModdingHelper");
+
+ token.ThrowIfCancellationRequested();
+
+ var latest = releases
+ .Select(r => new GitHubUpdateAvailableInfo(r))
+ .OrderByDescending(t => t.Version)
+ .ThenBy(t => t.Release.Prerelease)
+ .First();
+
+ if (App.Version < latest.Version)
+ return latest;
+ }
+ catch { }
+
+ return null;
+ }
+
+ public async Task DownloadUpdate(UpdateAvailableInfo u, string downloadedFile, IProgress progress, CancellationToken token = default)
+ {
+ if (u is not GitHubUpdateAvailableInfo update)
+ throw new ArgumentException("update");
+
+ if (File.Exists(downloadedFile))
+ File.Delete(downloadedFile);
+
+ using var client = new System.Net.WebClient();
+
+ client.DownloadProgressChanged += (sender, e) =>
+ {
+ // Update caller with download progress
+ progress.Report(e.ProgressPercentage / 100d);
+ };
+
+ // TODO: Select appropriate asset
+ var asset = update.Release.Assets[0];
+
+ await client.DownloadFileTaskAsync(new Uri(asset.BrowserDownloadUrl), downloadedFile);
+ }
+
+ private record GitHubUpdateAvailableInfo(string Name, ReleaseVersion Version, DateTimeOffset? ReleasedAt, Release Release)
+ : UpdateAvailableInfo(Name, Version, ReleasedAt)
+ {
+ public GitHubUpdateAvailableInfo(Release release)
+ : this(release.Name, ReleaseVersion.Parse(release.TagName), release.PublishedAt, release)
+ {
+ }
+ };
+}
diff --git a/ZuneModdingHelper/Services/IUpdateService.cs b/ZuneModdingHelper/Services/IUpdateService.cs
new file mode 100644
index 0000000..c674c47
--- /dev/null
+++ b/ZuneModdingHelper/Services/IUpdateService.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using ZuneModCore;
+
+namespace ZuneModdingHelper.Services;
+
+public interface IUpdateService
+{
+ Task FetchAvailableUpdate(CancellationToken token = default);
+
+ Task DownloadUpdate(UpdateAvailableInfo update, string destinationDirectory, IProgress progress, CancellationToken token = default);
+}
+
+public record UpdateAvailableInfo(string Name, ReleaseVersion Version, DateTimeOffset? ReleasedAt);