2026-03-02 00:44:29 +01:00
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Text.Json;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using Wino.Core.Domain;
|
|
|
|
|
using Wino.Core.Domain.Interfaces;
|
|
|
|
|
using Wino.Core.Domain.Models.Updates;
|
2026-03-05 10:12:03 +01:00
|
|
|
using Wino.Services.Migrations;
|
2026-03-02 00:44:29 +01:00
|
|
|
|
|
|
|
|
namespace Wino.Services;
|
|
|
|
|
|
|
|
|
|
public class UpdateManager : IUpdateManager
|
|
|
|
|
{
|
|
|
|
|
private const string UpdateNotesResourcePath = "ms-appx:///Assets/UpdateNotes/vnext.json";
|
2026-03-05 10:12:03 +01:00
|
|
|
private const string FeaturesResourcePath = "ms-appx:///Assets/UpdateNotes/features.json";
|
2026-03-02 00:44:29 +01:00
|
|
|
private const string UpdateNotesSeenKeyFormat = "UpdateNotes_{0}_Shown";
|
|
|
|
|
private const string MigrationCompletedKeyFormat = "Migration_{0}_Completed";
|
|
|
|
|
|
|
|
|
|
private readonly IFileService _fileService;
|
|
|
|
|
private readonly IConfigurationService _configurationService;
|
|
|
|
|
private readonly INativeAppService _nativeAppService;
|
|
|
|
|
private readonly List<IAppMigration> _migrations = [];
|
|
|
|
|
|
|
|
|
|
private string _versionSeenKey = string.Empty;
|
2026-03-05 10:12:03 +01:00
|
|
|
private UpdateNotes _latestUpdateNotes = new();
|
2026-03-02 00:44:29 +01:00
|
|
|
|
|
|
|
|
public UpdateManager(IFileService fileService,
|
|
|
|
|
IConfigurationService configurationService,
|
|
|
|
|
INativeAppService nativeAppService)
|
|
|
|
|
{
|
|
|
|
|
_fileService = fileService;
|
|
|
|
|
_configurationService = configurationService;
|
|
|
|
|
_nativeAppService = nativeAppService;
|
2026-03-05 10:12:03 +01:00
|
|
|
_migrations.Add(new VNextDelayMigration());
|
2026-03-02 00:44:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private string GetVersionSeenKey()
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(_versionSeenKey))
|
|
|
|
|
{
|
|
|
|
|
var version = _nativeAppService.GetFullAppVersion();
|
|
|
|
|
var sanitized = version.Replace(".", "_");
|
|
|
|
|
_versionSeenKey = string.Format(UpdateNotesSeenKeyFormat, sanitized);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return _versionSeenKey;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<UpdateNotes> GetLatestUpdateNotesAsync()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var json = await _fileService.GetFileContentByApplicationUriAsync(UpdateNotesResourcePath);
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(json))
|
2026-03-05 10:12:03 +01:00
|
|
|
{
|
|
|
|
|
_latestUpdateNotes = new UpdateNotes();
|
|
|
|
|
return _latestUpdateNotes;
|
|
|
|
|
}
|
2026-03-02 00:44:29 +01:00
|
|
|
|
2026-03-05 10:12:03 +01:00
|
|
|
_latestUpdateNotes = JsonSerializer.Deserialize(json, BasicTypesJsonContext.Default.UpdateNotes) ?? new UpdateNotes();
|
|
|
|
|
return _latestUpdateNotes;
|
2026-03-02 00:44:29 +01:00
|
|
|
}
|
|
|
|
|
catch (Exception)
|
|
|
|
|
{
|
2026-03-05 10:12:03 +01:00
|
|
|
_latestUpdateNotes = new UpdateNotes();
|
|
|
|
|
return _latestUpdateNotes;
|
2026-03-02 00:44:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-05 10:12:03 +01:00
|
|
|
// T
|
2026-03-02 00:44:29 +01:00
|
|
|
public bool ShouldShowUpdateNotes()
|
|
|
|
|
=> !_configurationService.Get(GetVersionSeenKey(), false);
|
|
|
|
|
|
2026-03-05 10:12:03 +01:00
|
|
|
public async Task<List<UpdateNoteSection>> GetFeaturesAsync()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var json = await _fileService.GetFileContentByApplicationUriAsync(FeaturesResourcePath);
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(json))
|
|
|
|
|
return [];
|
|
|
|
|
|
|
|
|
|
return JsonSerializer.Deserialize(json, BasicTypesJsonContext.Default.ListUpdateNoteSection) ?? [];
|
|
|
|
|
}
|
|
|
|
|
catch (Exception)
|
|
|
|
|
{
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-02 00:44:29 +01:00
|
|
|
public void MarkUpdateNotesAsSeen()
|
|
|
|
|
=> _configurationService.Set(GetVersionSeenKey(), true);
|
|
|
|
|
|
|
|
|
|
public bool HasPendingMigrations()
|
2026-03-05 10:12:03 +01:00
|
|
|
{
|
|
|
|
|
if (!_latestUpdateNotes.HasPendingMigrations)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return _migrations.Any(m => !_configurationService.Get(string.Format(MigrationCompletedKeyFormat, m.MigrationId), false));
|
|
|
|
|
}
|
2026-03-02 00:44:29 +01:00
|
|
|
|
|
|
|
|
public async Task RunPendingMigrationsAsync()
|
|
|
|
|
{
|
2026-03-05 10:12:03 +01:00
|
|
|
if (!_latestUpdateNotes.HasPendingMigrations)
|
|
|
|
|
_latestUpdateNotes = await GetLatestUpdateNotesAsync();
|
|
|
|
|
|
|
|
|
|
if (!_latestUpdateNotes.HasPendingMigrations)
|
|
|
|
|
return;
|
|
|
|
|
|
2026-03-02 00:44:29 +01:00
|
|
|
foreach (var migration in _migrations)
|
|
|
|
|
{
|
|
|
|
|
var key = string.Format(MigrationCompletedKeyFormat, migration.MigrationId);
|
|
|
|
|
|
|
|
|
|
if (!_configurationService.Get(key, false))
|
|
|
|
|
{
|
|
|
|
|
await migration.ExecuteAsync();
|
|
|
|
|
_configurationService.Set(key, true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void RegisterMigrations(IEnumerable<IAppMigration> migrations)
|
|
|
|
|
=> _migrations.AddRange(migrations);
|
|
|
|
|
}
|