feat(ui): add UpdateCheckService
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\ClaudeDo.Data\ClaudeDo.Data.csproj" />
|
<ProjectReference Include="..\ClaudeDo.Data\ClaudeDo.Data.csproj" />
|
||||||
|
<ProjectReference Include="..\ClaudeDo.Releases\ClaudeDo.Releases.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
73
src/ClaudeDo.Ui/Services/UpdateCheckService.cs
Normal file
73
src/ClaudeDo.Ui/Services/UpdateCheckService.cs
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
using ClaudeDo.Releases;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
|
||||||
|
namespace ClaudeDo.Ui.Services;
|
||||||
|
|
||||||
|
public enum UpdateCheckStatus
|
||||||
|
{
|
||||||
|
NeverChecked,
|
||||||
|
CheckFailed,
|
||||||
|
UpToDate,
|
||||||
|
UpdateAvailable,
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed partial class UpdateCheckService : ObservableObject
|
||||||
|
{
|
||||||
|
private readonly IReleaseClient _releases;
|
||||||
|
|
||||||
|
[ObservableProperty] private bool _isUpdateAvailable;
|
||||||
|
[ObservableProperty] private string? _latestVersion;
|
||||||
|
[ObservableProperty] private string _currentVersion;
|
||||||
|
[ObservableProperty] private bool _isChecking;
|
||||||
|
[ObservableProperty] private UpdateCheckStatus _lastCheckStatus = UpdateCheckStatus.NeverChecked;
|
||||||
|
|
||||||
|
public UpdateCheckService(IReleaseClient releases, string currentVersion)
|
||||||
|
{
|
||||||
|
_releases = releases;
|
||||||
|
_currentVersion = currentVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task CheckNowAsync(CancellationToken ct)
|
||||||
|
{
|
||||||
|
IsChecking = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
GiteaRelease? rel;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
rel = await _releases.GetLatestReleaseAsync(ct);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
LastCheckStatus = UpdateCheckStatus.CheckFailed;
|
||||||
|
IsUpdateAvailable = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rel is null)
|
||||||
|
{
|
||||||
|
LastCheckStatus = UpdateCheckStatus.CheckFailed;
|
||||||
|
IsUpdateAvailable = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var latest = (rel.TagName ?? "").TrimStart('v', 'V');
|
||||||
|
var cmp = VersionComparer.Compare(latest, CurrentVersion);
|
||||||
|
if (cmp.IsNewer)
|
||||||
|
{
|
||||||
|
LatestVersion = latest;
|
||||||
|
IsUpdateAvailable = true;
|
||||||
|
LastCheckStatus = UpdateCheckStatus.UpdateAvailable;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
IsUpdateAvailable = false;
|
||||||
|
LastCheckStatus = UpdateCheckStatus.UpToDate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
IsChecking = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
62
tests/ClaudeDo.Ui.Tests/Services/UpdateCheckServiceTests.cs
Normal file
62
tests/ClaudeDo.Ui.Tests/Services/UpdateCheckServiceTests.cs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
using System.Net.Http;
|
||||||
|
using ClaudeDo.Releases;
|
||||||
|
using ClaudeDo.Ui.Services;
|
||||||
|
|
||||||
|
namespace ClaudeDo.Ui.Tests.Services;
|
||||||
|
|
||||||
|
public class UpdateCheckServiceTests
|
||||||
|
{
|
||||||
|
private sealed class FakeReleaseClient : IReleaseClient
|
||||||
|
{
|
||||||
|
public GiteaRelease? Release { get; set; }
|
||||||
|
public bool Throw { get; set; }
|
||||||
|
|
||||||
|
public Task<GiteaRelease?> GetLatestReleaseAsync(CancellationToken ct)
|
||||||
|
{
|
||||||
|
if (Throw) throw new HttpRequestException();
|
||||||
|
return Task.FromResult(Release);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task DownloadAsync(string url, string destPath, IProgress<long> progress, CancellationToken ct)
|
||||||
|
=> throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Check_NewerRelease_SetsUpdateAvailable()
|
||||||
|
{
|
||||||
|
var svc = new UpdateCheckService(new FakeReleaseClient
|
||||||
|
{
|
||||||
|
Release = new GiteaRelease("v0.3.0", "r", new[] { new ReleaseAsset("ClaudeDo-0.3.0-win-x64.zip", "u", 1) }),
|
||||||
|
},
|
||||||
|
currentVersion: "0.1.0");
|
||||||
|
|
||||||
|
await svc.CheckNowAsync(CancellationToken.None);
|
||||||
|
|
||||||
|
Assert.Equal(UpdateCheckStatus.UpdateAvailable, svc.LastCheckStatus);
|
||||||
|
Assert.True(svc.IsUpdateAvailable);
|
||||||
|
Assert.Equal("0.3.0", svc.LatestVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Check_SameRelease_SetsUpToDate()
|
||||||
|
{
|
||||||
|
var svc = new UpdateCheckService(new FakeReleaseClient
|
||||||
|
{
|
||||||
|
Release = new GiteaRelease("v0.1.0", "r", new[] { new ReleaseAsset("ClaudeDo-0.1.0-win-x64.zip", "u", 1) }),
|
||||||
|
},
|
||||||
|
currentVersion: "0.1.0");
|
||||||
|
|
||||||
|
await svc.CheckNowAsync(CancellationToken.None);
|
||||||
|
|
||||||
|
Assert.Equal(UpdateCheckStatus.UpToDate, svc.LastCheckStatus);
|
||||||
|
Assert.False(svc.IsUpdateAvailable);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Check_NetworkError_SetsCheckFailedButDoesNotThrow()
|
||||||
|
{
|
||||||
|
var svc = new UpdateCheckService(new FakeReleaseClient { Throw = true }, "0.1.0");
|
||||||
|
await svc.CheckNowAsync(CancellationToken.None);
|
||||||
|
Assert.Equal(UpdateCheckStatus.CheckFailed, svc.LastCheckStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user