using System.Net.Http.Json;
using FCPModUpdater.Models;
namespace FCPModUpdater.Services;
public class UpdateCheckService(HttpClient httpClient)
{
private const string RepoOwner = "FalloutCollaborationProject";
private const string RepoName = "FCP-Mod-Updater";
///
/// Check if a newer version is available. Returns null if check fails or no update needed.
/// Checks both stable and prerelease versions.
///
public async Task CheckForUpdateAsync(CancellationToken ct = default)
{
try
{
var url = $"repos/{RepoOwner}/{RepoName}/releases?per_page=15";
var releases = await httpClient.GetFromJsonAsync>(url, ct);
if (releases is null || releases.Count == 0)
return null;
// Find the release with the highest semver (stable or prerelease)
ReleaseInfo? newest = null;
Version? newestVersion = null;
foreach (ReleaseInfo release in releases)
{
if (release.Draft)
continue;
var tagBase = GetBaseVersion(release.TagName.TrimStart('v'));
if (!Version.TryParse(tagBase, out Version? ver))
continue;
if (newestVersion is null || ver > newestVersion)
{
newestVersion = ver;
newest = release;
}
}
if (newest is null || newestVersion is null)
return null;
var currentBase = GetBaseVersion(AppVersion.SemanticVersion);
if (!Version.TryParse(currentBase, out Version? currentVer))
return null;
if (newestVersion > currentVer)
{
var latestTag = newest.TagName.TrimStart('v');
return new UpdateCheckResult(
CurrentVersion: AppVersion.InformationalVersion,
LatestVersion: latestTag,
ReleaseUrl: newest.HtmlUrl,
ReleaseName: newest.Name,
PublishedAt: newest.PublishedAt,
IsPrerelease: newest.Prerelease
);
}
return null;
}
catch
{
// Update check is non-critical — fail silently
return null;
}
}
private static string GetBaseVersion(string version)
{
var dashIndex = version.IndexOf('-');
return dashIndex >= 0 ? version[..dashIndex] : version;
}
}
public record UpdateCheckResult(
string CurrentVersion,
string LatestVersion,
string ReleaseUrl,
string ReleaseName,
DateTimeOffset PublishedAt,
bool IsPrerelease
);