refactors

This commit is contained in:
Jose Conde
2025-09-30 15:57:56 +02:00
parent 5e16f781b4
commit 4c76f04d0e
31 changed files with 403 additions and 293 deletions

View File

@@ -3,6 +3,7 @@ using Microsoft.Extensions.Hosting;
using ModVersionChecker.managers.filesystem; using ModVersionChecker.managers.filesystem;
using ModVersionChecker.managers.interfaces; using ModVersionChecker.managers.interfaces;
using ModVersionChecker.managers.litedb; using ModVersionChecker.managers.litedb;
using ModVersionChecker.repository.api;
using ModVersionChecker.service; using ModVersionChecker.service;
using ModVersionChecker.service.interfaces; using ModVersionChecker.service.interfaces;
using ModVersionChecker.ui.forms; using ModVersionChecker.ui.forms;
@@ -19,16 +20,19 @@ namespace ModVersionChecker
builder.ConfigureServices(services => builder.ConfigureServices(services =>
{ {
services.AddSingleton<IConfigManager, ConfigLiteDb>(); services.AddSingleton<IConfigRepository, ConfigLiteDb>();
services.AddSingleton<IAppsManager, AppLiteDb>(); services.AddSingleton<IAppRepository, AppLiteDb>();
services.AddSingleton<ISourcesDefManager, SourcesLiteDb>(); services.AddSingleton<ISourcesDefManager, SourcesLiteDb>();
services.AddSingleton<IFlightSimsManager, TypeLiteDb>(); services.AddSingleton<ITypeRepository, TypeLiteDb>();
services.AddSingleton<ITypeManager, TypeConfigLiteDb>(); services.AddSingleton<ITypeConfigRepository, TypeConfigLiteDb>();
services.AddSingleton<IFormFactory, FormFactory>(); services.AddSingleton<IFormFactory, FormFactory>();
services.AddSingleton<IAppStatusManager, AppStatusManager>(); services.AddSingleton<IAppStatusRepository, AppStatusManager>();
services.AddSingleton<INotifyIconService, NotifyIconService>(); services.AddSingleton<INotificationService, NotificationService>();
services.AddSingleton<IApiService, ApiService>(); services.AddSingleton<IApiService, ApiService>();
services.AddSingleton<IVersionService, VersionService>(); services.AddSingleton<IVersionService, VersionService>();
services.AddSingleton<IApiRepository, ApiRepository>();
services.AddSingleton<IStateService, StateService>();
services.AddSingleton<IAppService, AppService>();
services.AddTransient<MainForm>(); services.AddTransient<MainForm>();
services.AddTransient<AppDetailsForm>(); services.AddTransient<AppDetailsForm>();
@@ -59,9 +63,10 @@ namespace ModVersionChecker
var serviceProvider = host.Services; var serviceProvider = host.Services;
var configForm = serviceProvider.GetService<MainForm>(); var configForm = serviceProvider.GetService<MainForm>();
var notifyIconService = serviceProvider.GetRequiredService<INotifyIconService>(); var notifyIconService = serviceProvider.GetRequiredService<INotificationService>();
var configManager = serviceProvider.GetRequiredService<IConfigManager>(); var configManager = serviceProvider.GetRequiredService<IConfigRepository>();
var versionService = serviceProvider.GetRequiredService<IVersionService>(); var versionService = serviceProvider.GetRequiredService<IVersionService>();
var appService = serviceProvider.GetRequiredService<IAppService>();
var config = configManager.GetConfig(); var config = configManager.GetConfig();
EventHandler openFormHandler = (s, e) => EventHandler openFormHandler = (s, e) =>
@@ -101,9 +106,9 @@ namespace ModVersionChecker
if (checkOnInitialStart) if (checkOnInitialStart)
{ {
Task.Run(async () => Task.Run(() =>
{ {
await versionService.CheckAllApps(); appService.CheckAllApps();
if (configForm != null) if (configForm != null)
{ {
configForm.UpdateListView(); configForm.UpdateListView();

View File

@@ -7,34 +7,31 @@ namespace ModVersionChecker
{ {
public class VersionChecker public class VersionChecker
{ {
private readonly IConfigManager _configManager; private readonly IAppService _appService;
private readonly IConfigRepository _configManager;
private readonly IVersionService _versionService; private readonly IVersionService _versionService;
public event EventHandler<string>? OnFinished; public event EventHandler<string>? OnFinished;
public VersionChecker( public VersionChecker(
IAppService appService,
IVersionService versionService, IVersionService versionService,
IConfigManager configManager) IConfigRepository configManager
)
{ {
_configManager = configManager ?? throw new ArgumentNullException(nameof(configManager)); _configManager = configManager ?? throw new ArgumentNullException(nameof(configManager));
_versionService = versionService ?? throw new ArgumentNullException(nameof(versionService)); _versionService = versionService ?? throw new ArgumentNullException(nameof(versionService));
_appService = appService ?? throw new ArgumentNullException(nameof(appService));
} }
//private void HandleAppError(string message, AppConfig app)
//{
// errorMessages.Add(message);
// _appsManager.UpdateStatus(app, AppStatus.ERROR);
//}
public void StartVersionChecking() public void StartVersionChecking()
{ {
var config = _configManager.Load() ?? new Config(); var config = _configManager.Load() ?? new Config();
// Run version checks in a background thread new Thread(() =>
new Thread(async () =>
{ {
while (true) while (true)
{ {
await _versionService.CheckAllApps(); _appService.CheckAllApps();
Thread.Sleep(config.IntervalMinutes * 60 * 1000); Thread.Sleep(config.IntervalMinutes * 60 * 1000);
} }
@@ -42,95 +39,5 @@ namespace ModVersionChecker
}) })
{ IsBackground = true }.Start(); { IsBackground = true }.Start();
} }
//public async Task CheckAsync()
//{
// var config = _configManager.Load() ?? new GlobalConfig();
// var apps = _appsManager.Load() ?? new List<AppConfig>();
// var sources = _sourcesDefManager.List() ?? new List<SourceDef>();
// var fsMods = _fsManager.Load() ?? new List<TypeDef>();
// var types = _typeConfigManager.GetTypeConfigs() ?? new List<TypeConfig>();
// var appVersionsMap = await _apiVersionService.GetAppVersionsAsync(apps);
// updateMessages = new List<string>();
// errorMessages = new List<string>();
// foreach (AppConfig app in apps)
// {
// if (app.Status != AppStatus.ERROR && app.LastCheckedAt != 0 && app.LastCheckedAt < TimeUtils.GetUnixTimeMillis(DateTime.Now.AddMinutes(-60)))
// continue;
// var status = AppStatus.NONE;
// var sourceId = app.Source;
// // Skip apps that are not in the API response
// if (!appVersionsMap.Any(a => app.Id == a.Id)) {
// continue;
// }
// var latesstVersion = appVersionsMap.FirstOrDefault(a => a.Id == app.Id).LatestVersion;
// if (string.IsNullOrWhiteSpace(sourceId))
// {
// HandleAppError($"{app.Name} has no source configured.", app);
// continue;
// }
// var source = sources.FirstOrDefault(s => s.Id == sourceId);
// if (source == null)
// {
// HandleAppError($"{app.Name} has an invalid source: {sourceId}", app);
// continue;
// }
// try
// {
// var type = app.Types[0];
// var typeConfig = types[0];
// if (typeConfig == null)
// {
// HandleAppError($"{app.Name} has no FS mod path configured for version {type}.", app);
// continue;
// }
// var current = NuGetVersion.Parse(VersionUtils.GetCurrentVersion(app, typeConfig));
// var latest = NuGetVersion.Parse(latesstVersion);
// app.CurrentVersion = current.ToString();
// app.LatestVersion = latest.ToString();
// if (latest.CompareTo(current) == 1)
// {
// updateMessages.Add($"{app.Name}: New version {latest} (current: {current})");
// status = AppStatus.UPDATE_AVAILABLE;
// }
// _appsManager.UpdateStatus(app, status);
// }
// catch (Exception ex)
// {
// HandleAppError($"Failed for {app.Name}: {ex.Message}", app);
// }
// }
// if (updateMessages.Count > 0)
// {
// _notifyIconService.ShowBalloonTip(
// 10000,
// "Updates Available",
// string.Join("\n", updateMessages),
// ToolTipIcon.Info
// );
// }
// if (errorMessages.Count > 0)
// {
// _notifyIconService.ShowBalloonTip(
// 10000,
// "Errors",
// string.Join("\n", errorMessages),
// ToolTipIcon.Error
// );
// }
// OnFinished?.Invoke(this, "Version check completed.");
//}
} }
} }

View File

@@ -141,7 +141,7 @@ namespace ModVersionChecker.repository.api
public async Task<List<AppResponse>?> SearchApps(string searchText) public async Task<List<AppResponse>?> SearchApps(string searchText)
{ {
var url = $"{baseUrl}/app/search?query={Uri.EscapeDataString(searchText)}"; var url = $"{baseUrl}/app/search?q={Uri.EscapeDataString(searchText)}";
var request = await CreateRequestAsync(HttpMethod.Get, url); var request = await CreateRequestAsync(HttpMethod.Get, url);
var response = await _httpClient.SendAsync(request); var response = await _httpClient.SendAsync(request);
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
@@ -149,7 +149,7 @@ namespace ModVersionChecker.repository.api
return JsonSerializer.Deserialize<List<AppResponse>>(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); return JsonSerializer.Deserialize<List<AppResponse>>(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
} }
public async Task<List<AppResponse>> GetAppsByIds(App[] apps) public async Task<List<AppResponse>> GetAppsByIds(List<App> apps)
{ {
var query = string.Join("&", apps.Select(a => $"id={Uri.EscapeDataString(a.Id)}")); var query = string.Join("&", apps.Select(a => $"id={Uri.EscapeDataString(a.Id)}"));
var url = $"{baseUrl}/app/search?{query}"; var url = $"{baseUrl}/app/search?{query}";

View File

@@ -12,6 +12,6 @@ namespace ModVersionChecker.repository.api
Task<List<AppResponse>?> SearchApps(string searchText); Task<List<AppResponse>?> SearchApps(string searchText);
Task<List<TypeResponse>> GetTypes(); Task<List<TypeResponse>> GetTypes();
Task<List<SourceResponse>> GetSources(); Task<List<SourceResponse>> GetSources();
Task<List<AppResponse>> GetAppsByIds(App[] apps); Task<List<AppResponse>> GetAppsByIds(List<App> apps);
} }
} }

View File

@@ -1,4 +1,5 @@
using ModVersionChecker.enums; using ModVersionChecker.enums;
using ModVersionChecker.utils;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace ModVersionChecker.repository.api.dto namespace ModVersionChecker.repository.api.dto
@@ -74,7 +75,8 @@ namespace ModVersionChecker.repository.api.dto
LatestVersion = appResponse.LatestVersion, LatestVersion = appResponse.LatestVersion,
Status = appResponse.Status, Status = appResponse.Status,
LastCheckedAt = appResponse.LastCheckedAt, LastCheckedAt = appResponse.LastCheckedAt,
LocalCheckedAt = TimeUtils.GetUnixTimeMillis(null)
}; };
} }
} }

View File

@@ -9,7 +9,7 @@ using System.Threading.Tasks;
namespace ModVersionChecker.managers.filesystem namespace ModVersionChecker.managers.filesystem
{ {
public class AppStatusManager : IAppStatusManager public class AppStatusManager : IAppStatusRepository
{ {
private Dictionary<string, AppStatus> _statuses = new Dictionary<string, AppStatus>(); private Dictionary<string, AppStatus> _statuses = new Dictionary<string, AppStatus>();

View File

@@ -6,7 +6,7 @@ using System.Text.Json;
namespace ModVersionChecker.managers.filesystem namespace ModVersionChecker.managers.filesystem
{ {
public class ConfigManager : IConfigManager public class ConfigManager : IConfigRepository
{ {
private static readonly string _filePath = Path.Combine(AppContext.BaseDirectory, "data", "config.json"); private static readonly string _filePath = Path.Combine(AppContext.BaseDirectory, "data", "config.json");

View File

@@ -7,7 +7,7 @@ using System.Threading.Tasks;
namespace ModVersionChecker.managers.interfaces namespace ModVersionChecker.managers.interfaces
{ {
public interface IAppsManager public interface IAppRepository
{ {
List<App> Load(); List<App> Load();

View File

@@ -7,7 +7,7 @@ using System.Threading.Tasks;
namespace ModVersionChecker.managers.interfaces namespace ModVersionChecker.managers.interfaces
{ {
public interface IAppStatusManager public interface IAppStatusRepository
{ {
List<AppStatus> Load(); List<AppStatus> Load();
void Save(List<AppStatus> appStatuses); void Save(List<AppStatus> appStatuses);

View File

@@ -7,7 +7,7 @@ using System.Threading.Tasks;
namespace ModVersionChecker.managers.interfaces namespace ModVersionChecker.managers.interfaces
{ {
public interface IConfigManager public interface IConfigRepository
{ {
Config Load(); Config Load();
void Save(Config config); void Save(Config config);

View File

@@ -2,7 +2,7 @@
namespace ModVersionChecker.managers.interfaces namespace ModVersionChecker.managers.interfaces
{ {
public interface ITypeManager public interface ITypeConfigRepository
{ {
List<TypeConfig> GetTypeConfigs(); List<TypeConfig> GetTypeConfigs();
void SaveTypeConfigs(List<TypeConfig> types); void SaveTypeConfigs(List<TypeConfig> types);

View File

@@ -7,7 +7,7 @@ using System.Threading.Tasks;
namespace ModVersionChecker.managers.interfaces namespace ModVersionChecker.managers.interfaces
{ {
public interface IFlightSimsManager public interface ITypeRepository
{ {
List<TypeResponse> Load(); List<TypeResponse> Load();
void Save(TypeResponse config); void Save(TypeResponse config);

View File

@@ -9,7 +9,7 @@ using System.Threading.Tasks;
namespace ModVersionChecker.managers.litedb namespace ModVersionChecker.managers.litedb
{ {
public class AppLiteDb : LiteDb, IAppsManager public class AppLiteDb : LiteDb, IAppRepository
{ {
protected override string collection => LiteDb.APPS_COLLECTION; protected override string collection => LiteDb.APPS_COLLECTION;

View File

@@ -3,7 +3,7 @@ using ModVersionChecker.model;
namespace ModVersionChecker.managers.litedb namespace ModVersionChecker.managers.litedb
{ {
public class ConfigLiteDb : LiteDb, IConfigManager public class ConfigLiteDb : LiteDb, IConfigRepository
{ {
protected override string collection => LiteDb.CONFIG_COLLECTION; protected override string collection => LiteDb.CONFIG_COLLECTION;
public Config Load() public Config Load()

View File

@@ -3,7 +3,7 @@ using ModVersionChecker.model;
namespace ModVersionChecker.managers.litedb namespace ModVersionChecker.managers.litedb
{ {
internal class TypeConfigLiteDb : LiteDb, ITypeManager internal class TypeConfigLiteDb : LiteDb, ITypeConfigRepository
{ {
protected override string collection => TYPES_COLLECTION; protected override string collection => TYPES_COLLECTION;

View File

@@ -8,7 +8,7 @@ using System.Threading.Tasks;
namespace ModVersionChecker.managers.litedb namespace ModVersionChecker.managers.litedb
{ {
internal class TypeLiteDb : LiteDb, IFlightSimsManager internal class TypeLiteDb : LiteDb, ITypeRepository
{ {
protected override string collection => FLIGHT_SIMS_COLLECTION; protected override string collection => FLIGHT_SIMS_COLLECTION;

View File

@@ -31,16 +31,16 @@ namespace ModVersionChecker.service
public Task<List<SourceResponse>> GetSources() public Task<List<SourceResponse>> GetSources()
=> _apiRepository.GetSources(); => _apiRepository.GetSources();
public async Task<List<App>> GetAppsByIds(App[] apps) public async Task<List<App>> GetAppsByIds(List<App> apps)
{ {
var appResponses = await _apiRepository.GetAppsByIds(apps); var appResponses = await _apiRepository.GetAppsByIds(apps);
return appResponses.Select(AppResponse.toModel).ToList(); return appResponses.Select(AppResponse.toModel).ToList();
} }
public Task<List<App>?> SearchApps(string searchText) public async Task<List<App>> SearchApps(string searchText)
{ {
var appResponses = _apiRepository.SearchApps(searchText); var appResponses = await _apiRepository.SearchApps(searchText);
return appResponses.ContinueWith(t => t.Result?.Select(AppResponse.toModel).ToList()); return appResponses.Select(AppResponse.toModel).ToList();
} }
} }

View File

@@ -0,0 +1,203 @@
using ModVersionChecker.enums;
using ModVersionChecker.managers.interfaces;
using ModVersionChecker.model;
using ModVersionChecker.service.interfaces;
using ModVersionChecker.utils;
namespace ModVersionChecker.service
{
public class AppService: IAppService
{
private readonly IApiService _apiService;
private readonly IAppRepository _appRepository;
private readonly IVersionService _versionService;
private readonly IStateService _stateService;
private readonly INotificationService _notificationService;
private readonly Config _config;
public AppService(
IApiService apiService,
IVersionService versionService,
IAppRepository appManager,
IStateService stateService,
INotificationService notificationService
) {
_apiService = apiService;
_appRepository = appManager;
_versionService = versionService;
_stateService = stateService;
_notificationService = notificationService;
_config = _stateService.GetConfig();
}
public bool CreateApp(App? app)
{
if (app != null)
{
_appRepository.Insert(app);
return true;
}
return false;
}
public bool DeleteApp(App? app)
{
if (app !=null)
{
_appRepository.Delete(app.Id);
return true;
}
return false;
}
public bool UpdateApp(App? app)
{
if (app != null)
{
_appRepository.Update(app);
return true;
}
return false;
}
public async Task<List<App>> GetAndUpdateCurrentApps()
{
try
{
List<App> apps = _appRepository.Load();
if (apps == null) throw new InvalidOperationException("Failed to load apps: repository returned null.");
if (apps.Count == 0) return new List<App>();
List<App> updatedApps = await _apiService.GetAppsByIds(apps);
if (updatedApps == null) throw new InvalidOperationException("Failed to load apps: repository returned null.");
if (updatedApps.Count == 0) return apps;
foreach (App app in updatedApps)
{
app.CurrentVersion = _versionService.GetCurrentVersion(app);
CheckAppStatus(app);
_appRepository.Update(app);
}
_stateService.SetApps(updatedApps);
return updatedApps;
}
catch (Exception ex)
{
Console.WriteLine(ex);
return new List<App>();
}
}
public App CheckAppStatus(App app)
{
var status = AppStatus.NONE;
if (app.LatestVersion == null)
{
app.Status = AppStatus.ERROR;
_appRepository.Update(app);
return app;
}
var currentVersion = app.CurrentVersion;
var updateMessage = "";
if (_versionService.IsUpdateAvailable(currentVersion, app.LatestVersion))
{
updateMessage = $"{app.Name}: New version {app.LatestVersion} (current: {currentVersion})";
status = AppStatus.UPDATE_AVAILABLE;
}
app.Status = status;
app.LastCheckedAt = TimeUtils.GetUnixTimeMillis(null);
return app;
}
public void CheckAllApps()
{
var apps = _appRepository.Load();
var sources = _stateService.GetSources();
List<string> errorMessages = new List<string>();
List<string> updateMessages = new List<string>();
if (apps == null || apps.Count == 0)
{
return;
}
foreach (App app in apps)
{
var sourceId = app.Source;
if (
app.Status != AppStatus.ERROR && app.LastCheckedAt != 0 &&
app.LastCheckedAt < TimeUtils.GetUnixTimeMillis(DateTime.UtcNow.AddMinutes(-60)) ||
string.IsNullOrWhiteSpace(sourceId)
)
{
continue;
}
try
{
var latesstVersion = app.LatestVersion;
var source = sources.FirstOrDefault(s => s.Id == sourceId);
if (source == null)
{
errorMessages.Add($"{app.Name} has an invalid source: {sourceId}");
continue;
}
var typeConfig = _config.Types.FirstOrDefault(t => t.ShortName == app.Type);
if (typeConfig == null)
{
errorMessages.Add($"{app.Name} has no valid type config.");
continue;
}
app.CurrentVersion = VersionUtils.GetCurrentVersion(app, typeConfig);
var updatedApp = CheckAppStatus(app);
_appRepository.Update(updatedApp);
if (updatedApp.Status == AppStatus.UPDATE_AVAILABLE)
{
updateMessages.Add($"{app.Name}: New version {app.LatestVersion} (current: {app.CurrentVersion})");
}
}
catch (Exception ex)
{
errorMessages.Add($"Failed to check {app.Name}: {ex.Message}");
}
}
if (updateMessages.Count > 0)
{
_notificationService.ShowBalloonTip(
10000,
"Updates Available",
string.Join("\n", updateMessages),
ToolTipIcon.Info
);
}
if (errorMessages.Count > 0)
{
_notificationService.ShowBalloonTip(
10000,
"Errors",
string.Join("\n", errorMessages),
ToolTipIcon.Error
);
}
}
public List<App> PurgeExisitingApps(List<App> apps)
{
var _existingApps = _stateService.GetApps();
if (_existingApps == null || _existingApps.Count == 0)
{
return apps;
}
return apps.Where(a => !_existingApps.Any(e => e.Id == a.Id)).ToList();
}
}
}

View File

@@ -7,7 +7,7 @@ using System.Threading.Tasks;
namespace ModVersionChecker.service namespace ModVersionChecker.service
{ {
public class NotifyIconService : INotifyIconService public class NotificationService : INotificationService
{ {
private NotifyIcon? _notifyIcon; private NotifyIcon? _notifyIcon;
public void SetNotifyIcon(NotifyIcon icon) public void SetNotifyIcon(NotifyIcon icon)

View File

@@ -0,0 +1,41 @@
using ModVersionChecker.managers.interfaces;
using ModVersionChecker.model;
using ModVersionChecker.repository.api.dto;
using ModVersionChecker.service.interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ModVersionChecker.service
{
public class StateService: IStateService
{
private List<App> apps = new List<App>();
private List<SourceResponse> sources = new List<SourceResponse>();
private List<TypeResponse> types = new List<TypeResponse>();
private Config config = new Config();
private readonly IAppRepository _appRepository;
public StateService(
IAppRepository appRepository
) {
_appRepository = appRepository;
}
public List<App> GetApps() => apps;
public void SetApps(List<App> apps) => this.apps = apps;
public List<SourceResponse> GetSources() => sources;
public void SetSources(List<SourceResponse> sources) => this.sources = sources;
public List<TypeResponse> GetTypes() => types;
public void SetTypes(List<TypeResponse> types) => this.types = types;
public Config GetConfig() => config;
public void SetConfig(Config config) => this.config = config;
public void UpdateApps() => this.apps = _appRepository.Load();
}
}

View File

@@ -9,121 +9,25 @@ namespace ModVersionChecker.service
{ {
public class VersionService: IVersionService public class VersionService: IVersionService
{ {
private readonly IApiService _apiService;
private readonly IAppsManager _appsManager;
private readonly INotifyIconService _notifyIconService;
private readonly Config _globalConfig; private readonly Config _globalConfig;
public VersionService( public VersionService(
IApiService apiVersionService, IConfigRepository configManager
IAppsManager appsManager, ) {
IConfigManager configManager,
INotifyIconService notifyIconService)
{
_apiService = apiVersionService;
_appsManager = appsManager ?? throw new ArgumentNullException(nameof(appsManager));
_notifyIconService = notifyIconService ?? throw new ArgumentNullException(nameof(notifyIconService));
_globalConfig = configManager.Load() ?? new Config(); _globalConfig = configManager.Load() ?? new Config();
} }
public App CheckApp(App app) public string GetCurrentVersion(App app)
{ {
var status = AppStatus.NONE; var typeConfig = _globalConfig.Types.FirstOrDefault(t => t.ShortName == app.Type);
if (app.LatestVersion== null) if (typeConfig == null)
{ {
app.Status = AppStatus.ERROR; throw new InvalidOperationException($"No type config found for app type: {app.Type}");
_appsManager.Update(app);
return app;
} }
var currentVersion = app.CurrentVersion; return VersionUtils.GetCurrentVersion(app, typeConfig);
var updateMessage = "";
if (isUpdateAvailable(currentVersion, app.LatestVersion))
{
updateMessage = $"{app.Name}: New version {app.LatestVersion} (current: {currentVersion})";
status = AppStatus.UPDATE_AVAILABLE;
}
app.Status = status;
app.LastCheckedAt = TimeUtils.GetUnixTimeMillis(null);
_appsManager.Update(app);
return app;
} }
public async Task CheckAllApps() public bool IsUpdateAvailable(string currentVersion, string latestVersion)
{
var apps = _appsManager.Load();
var sources = await _apiService.GetSources();
var appVersionsMap = await _apiService.GetAppVersionsAsync(apps);
List<string> errorMessages = new List<string>();
List<string> updateMessages = new List<string>();
if (apps == null || apps.Count == 0)
{
return;
}
foreach (App app in apps)
{
var sourceId = app.Source;
if (
app.Status != AppStatus.ERROR && app.LastCheckedAt != 0 && app.LastCheckedAt < TimeUtils.GetUnixTimeMillis(DateTime.UtcNow.AddMinutes(-60)) ||
!app.Active || string.IsNullOrWhiteSpace(sourceId)
)
{
continue;
}
try
{
var latesstVersion = appVersionsMap.FirstOrDefault(a => a.Id == app.Id)?.LatestVersion;
var source = sources.FirstOrDefault(s => s.Id == sourceId);
if (source == null)
{
errorMessages.Add($"{app.Name} has an invalid source: {sourceId}");
continue;
}
var typeConfig = _globalConfig.Types.FirstOrDefault(t => t.ShortName == app.Type);
if (typeConfig == null)
{
errorMessages.Add($"{app.Name} has no valid type config.");
continue;
}
app.CurrentVersion = VersionUtils.GetCurrentVersion(app, typeConfig);
var updatedApp = CheckApp(app);
if (updatedApp.Status == AppStatus.UPDATE_AVAILABLE)
{
updateMessages.Add($"{app.Name}: New version {app.LatestVersion} (current: {app.CurrentVersion})");
}
}
catch (Exception ex)
{
errorMessages.Add($"Failed to check {app.Name}: {ex.Message}");
}
}
if (updateMessages.Count > 0)
{
_notifyIconService.ShowBalloonTip(
10000,
"Updates Available",
string.Join("\n", updateMessages),
ToolTipIcon.Info
);
}
if (errorMessages.Count > 0)
{
_notifyIconService.ShowBalloonTip(
10000,
"Errors",
string.Join("\n", errorMessages),
ToolTipIcon.Error
);
}
}
private bool isUpdateAvailable(string currentVersion, string latestVersion)
{ {
try try
{ {

View File

@@ -12,8 +12,7 @@ namespace ModVersionChecker.service.interfaces
Task<List<SourceResponse>> GetSources(); Task<List<SourceResponse>> GetSources();
Task<AppVersionsResponse?> GetAppLatestVersionAsync(App app); Task<AppVersionsResponse?> GetAppLatestVersionAsync(App app);
Task<bool> AuthenticateAsync(string username, string password); Task<bool> AuthenticateAsync(string username, string password);
Task<List<App>> GetAppsByIds(App[] apps); Task<List<App>> GetAppsByIds(List<App> apps);
Task<List<App>?> SearchApps(string searchText); Task<List<App>> SearchApps(string searchText);
} }
} }

View File

@@ -0,0 +1,20 @@
namespace ModVersionChecker.service.interfaces
{
public interface IAppService
{
// List<App> GetAppsById(List<App> apps);
// bool SaveApps(List<App> apps);
// App? GetAppById(string id);
bool CreateApp(App? app);
bool UpdateApp(App? app);
bool DeleteApp(App? app);
Task<List<App>> GetAndUpdateCurrentApps();
App CheckAppStatus(App app);
void CheckAllApps();
List<App> PurgeExisitingApps(List<App> apps);
}
}

View File

@@ -6,7 +6,7 @@ using System.Threading.Tasks;
namespace ModVersionChecker.service.interfaces namespace ModVersionChecker.service.interfaces
{ {
public interface INotifyIconService public interface INotificationService
{ {
void SetNotifyIcon(NotifyIcon icon); void SetNotifyIcon(NotifyIcon icon);
void ShowBalloonTip(int millis, string title, string message, ToolTipIcon icon); void ShowBalloonTip(int millis, string title, string message, ToolTipIcon icon);

View File

@@ -0,0 +1,20 @@
using ModVersionChecker.repository.api.dto;
using ModVersionChecker.model;
namespace ModVersionChecker.service.interfaces
{
public interface IStateService
{
List<App> GetApps();
void SetApps(List<App> apps);
void UpdateApps();
List<SourceResponse> GetSources();
void SetSources(List<SourceResponse> sources);
List<TypeResponse> GetTypes();
void SetTypes(List<TypeResponse> types);
Config GetConfig();
void SetConfig(Config config);
}
}

View File

@@ -8,8 +8,12 @@ namespace ModVersionChecker.service.interfaces
{ {
public interface IVersionService public interface IVersionService
{ {
App CheckApp(App app); //App CheckAppStatus(App app);
Task CheckAllApps(); //Task CheckAllApps();
string GetCurrentVersion(App app);
bool IsUpdateAvailable(string currentVersion, string latestVersion);
} }
} }

View File

@@ -9,15 +9,18 @@ namespace ModVersionChecker.ui.forms
private ListBox _resultsList; private ListBox _resultsList;
private Button _addButton; private Button _addButton;
private readonly IApiService _apiService; private readonly IApiService _apiService;
private readonly IAppsManager _appsManager; private readonly IAppService _appService;
public App? SelectedApp { get; private set; } public App? SelectedApp { get; private set; }
public event EventHandler<string> OnAppAdded; public event EventHandler<string> OnAppAdded;
public AppDetailsForm(IApiService apiService, IAppsManager appsManager) public AppDetailsForm(
IApiService apiService,
IAppService appService
)
{ {
_apiService = apiService ?? throw new ArgumentNullException(nameof(apiService)); _apiService = apiService ?? throw new ArgumentNullException(nameof(apiService));
_appsManager = appsManager ?? throw new ArgumentNullException(nameof(appsManager)); _appService = appService ?? throw new ArgumentNullException(nameof(appService));
InitializeComponent(); InitializeComponent();
} }
@@ -27,6 +30,7 @@ namespace ModVersionChecker.ui.forms
Width = 400; Width = 400;
Height = 500; Height = 500;
Padding = new Padding(20); Padding = new Padding(20);
StartPosition = FormStartPosition.CenterParent;
_searchBox = new TextBox { Dock = DockStyle.Top, PlaceholderText = "Search..." }; _searchBox = new TextBox { Dock = DockStyle.Top, PlaceholderText = "Search..." };
_resultsList = new ListBox { Dock = DockStyle.Fill }; _resultsList = new ListBox { Dock = DockStyle.Fill };
@@ -54,8 +58,8 @@ namespace ModVersionChecker.ui.forms
_resultsList.DataSource = null; _resultsList.DataSource = null;
return; return;
} }
var results = await _apiService.SearchApps(query); var apiResults = await _apiService.SearchApps(query);
_resultsList.DataSource = results; _resultsList.DataSource = _appService.PurgeExisitingApps(apiResults);
_resultsList.DisplayMember = "Name"; _resultsList.DisplayMember = "Name";
} }
@@ -64,7 +68,7 @@ namespace ModVersionChecker.ui.forms
if (_resultsList.SelectedItem is App app) if (_resultsList.SelectedItem is App app)
{ {
SelectedApp = app; SelectedApp = app;
_appsManager.Insert(app); _appService.CreateApp(app);
DialogResult = DialogResult.OK; DialogResult = DialogResult.OK;
OnAppAdded?.Invoke(this, "App saved"); OnAppAdded?.Invoke(this, "App saved");
Close(); Close();

View File

@@ -16,8 +16,8 @@ namespace ModVersionChecker.ui.forms
public AppDetailsForm CreateAppDetailsForm(App? app, bool isEditable, EventHandler<string>? onAppSaved) public AppDetailsForm CreateAppDetailsForm(App? app, bool isEditable, EventHandler<string>? onAppSaved)
{ {
var apiService = _serviceProvider.GetRequiredService<IApiService>(); var apiService = _serviceProvider.GetRequiredService<IApiService>();
var appsManager = _serviceProvider.GetRequiredService<IAppsManager>(); var appService= _serviceProvider.GetRequiredService<IAppService>();
var form = new AppDetailsForm(apiService, appsManager); var form = new AppDetailsForm(apiService, appService);
if (onAppSaved != null) if (onAppSaved != null)
{ {
form.OnAppAdded += onAppSaved; form.OnAppAdded += onAppSaved;
@@ -27,15 +27,15 @@ namespace ModVersionChecker.ui.forms
public GlobalConfigForm CreateGlobalConfigForm() public GlobalConfigForm CreateGlobalConfigForm()
{ {
var configManager = _serviceProvider.GetRequiredService<IConfigManager>(); var configManager = _serviceProvider.GetRequiredService<IConfigRepository>();
return new GlobalConfigForm(configManager); return new GlobalConfigForm(configManager);
} }
public TypeConfigForm CreateTypeConfigForm() public TypeConfigForm CreateTypeConfigForm()
{ {
var typeManager = _serviceProvider.GetRequiredService<ITypeManager>(); var typeManager = _serviceProvider.GetRequiredService<ITypeConfigRepository>();
var apiService = _serviceProvider.GetRequiredService<IApiService>(); var apiService = _serviceProvider.GetRequiredService<IApiService>();
var configManager = _serviceProvider.GetRequiredService<IConfigManager>(); var configManager = _serviceProvider.GetRequiredService<IConfigRepository>();
return new TypeConfigForm(typeManager, apiService, configManager); return new TypeConfigForm(typeManager, apiService, configManager);
} }

View File

@@ -5,7 +5,7 @@ namespace ModVersionChecker.ui.forms
{ {
public class GlobalConfigForm : Form public class GlobalConfigForm : Form
{ {
private IConfigManager _configManager; private IConfigRepository _configManager;
private Config _config; private Config _config;
private Label _millislabel, _checkStartupLabel, _runOnStartupLabel; private Label _millislabel, _checkStartupLabel, _runOnStartupLabel;
@@ -15,7 +15,7 @@ namespace ModVersionChecker.ui.forms
private TableLayoutPanel _mainLayout, _configsPanel; private TableLayoutPanel _mainLayout, _configsPanel;
private FlowLayoutPanel _buttonPanel; private FlowLayoutPanel _buttonPanel;
public GlobalConfigForm(IConfigManager configManager) public GlobalConfigForm(IConfigRepository configManager)
{ {
_configManager = configManager; _configManager = configManager;
_config = _configManager.GetConfig(); _config = _configManager.GetConfig();

View File

@@ -1,7 +1,6 @@
using ModVersionChecker.enums; using ModVersionChecker.enums;
using ModVersionChecker.managers.interfaces; using ModVersionChecker.managers.interfaces;
using ModVersionChecker.model; using ModVersionChecker.model;
using ModVersionChecker.repository.api.dto;
using ModVersionChecker.service.interfaces; using ModVersionChecker.service.interfaces;
using ModVersionChecker.utils; using ModVersionChecker.utils;
@@ -10,15 +9,13 @@ namespace ModVersionChecker.ui.forms
{ {
public class MainForm : Form public class MainForm : Form
{ {
private readonly IAppsManager _appsManager; private readonly IStateService _stateService;
private readonly IFormFactory _formFactory; private readonly IFormFactory _formFactory;
private readonly IFlightSimsManager _fsManager;
private readonly IApiService _apiService; private readonly IApiService _apiService;
private readonly IVersionService _versionService; private readonly IAppService _appService;
private readonly TableLayoutPanel _mainLayout; private readonly TableLayoutPanel _mainLayout;
private readonly Config _globalConfig; private readonly Config _globalConfig;
private List<App> _apps = new List<App>();
private ListView _listView; private ListView _listView;
private ImageList _statusImageList = new ImageList(); private ImageList _statusImageList = new ImageList();
@@ -26,27 +23,20 @@ namespace ModVersionChecker.ui.forms
public event EventHandler<string> OnRecheck; public event EventHandler<string> OnRecheck;
private EventHandler<string> onAppSavedHandler; private EventHandler<string> onAppSavedHandler;
private MenuStrip _menuStrip; private MenuStrip _menuStrip;
private List<TypeResponse> _fsMods;
private readonly Dictionary<string, TextBox> _fsModPathTextBoxes = new Dictionary<string, TextBox>(); private readonly Dictionary<string, TextBox> _fsModPathTextBoxes = new Dictionary<string, TextBox>();
private List<SourceResponse> _sources = new List<SourceResponse>();
private List<TypeResponse> _typesDef = new List<TypeResponse>();
public MainForm( public MainForm(
IConfigManager configManager, IStateService stateService,
IAppsManager appsManager, IAppService appService,
IConfigRepository configManager,
IFormFactory formFactory, IFormFactory formFactory,
IFlightSimsManager fsManager, IApiService apiService
IApiService apiService, )
ITypeManager typeConfigManager,
IVersionService versionService)
{ {
_appsManager = appsManager ?? throw new ArgumentNullException(nameof(appsManager));
_formFactory = formFactory ?? throw new ArgumentNullException(nameof(formFactory)); _formFactory = formFactory ?? throw new ArgumentNullException(nameof(formFactory));
_fsManager = fsManager ?? throw new ArgumentNullException(nameof(fsManager));
_apiService = apiService ?? throw new ArgumentNullException(nameof(apiService)); _apiService = apiService ?? throw new ArgumentNullException(nameof(apiService));
_versionService = versionService ?? throw new ArgumentNullException(nameof(versionService)); _appService = appService ?? throw new ArgumentNullException(nameof(appService));
_fsMods = _fsManager.Load(); _stateService = stateService ?? throw new ArgumentNullException(nameof(stateService));
_statusImageList.Images.Add("none", new Icon("Resources/ok-icon.ico")); _statusImageList.Images.Add("none", new Icon("Resources/ok-icon.ico"));
_statusImageList.Images.Add("update", new Icon("Resources/up-icon.ico")); _statusImageList.Images.Add("update", new Icon("Resources/up-icon.ico"));
@@ -74,6 +64,7 @@ namespace ModVersionChecker.ui.forms
var app = form.SelectedApp; var app = form.SelectedApp;
UpdateCurrentVersion(app); UpdateCurrentVersion(app);
_stateService.UpdateApps();
UpdateListView(); UpdateListView();
OnConfigChanged?.Invoke(this, EventArgs.Empty); OnConfigChanged?.Invoke(this, EventArgs.Empty);
}; };
@@ -81,6 +72,22 @@ namespace ModVersionChecker.ui.forms
this.Load += MainForm_LoadAsync; this.Load += MainForm_LoadAsync;
} }
private async void MainForm_LoadAsync(object? sender, EventArgs e)
{
await _apiService.AuthenticateAsync("user", "user");
var apps = await _appService.GetAndUpdateCurrentApps();
var sources = await _apiService.GetSources();
var types = await _apiService.GetTypes();
_stateService.SetApps(apps);
_stateService.SetSources(sources);
_stateService.SetTypes(types);
_stateService.SetConfig(_globalConfig);
UpdateListView();
}
private void UpdateCurrentVersion(App app) private void UpdateCurrentVersion(App app)
{ {
TypeConfig? typeConfig = _globalConfig.Types.FirstOrDefault(tc => app.Type == tc.ShortName); TypeConfig? typeConfig = _globalConfig.Types.FirstOrDefault(tc => app.Type == tc.ShortName);
@@ -92,21 +99,12 @@ namespace ModVersionChecker.ui.forms
app.CurrentVersion = versionInDisk; app.CurrentVersion = versionInDisk;
app.LastCheckedAt = TimeUtils.GetUnixTimeMillis(DateTime.Now); app.LastCheckedAt = TimeUtils.GetUnixTimeMillis(DateTime.Now);
_versionService.CheckApp(app); _appService.CheckAppStatus(app);
_appService.UpdateApp(app);
} }
} }
private async void MainForm_LoadAsync(object? sender, EventArgs e)
{
await _apiService.AuthenticateAsync("user", "user");
_apps = await _apiService.GetAppsByIds([]);
_sources = await _apiService.GetSources();
_typesDef = await _apiService.GetTypes();
UpdateListView();
}
private TableLayoutPanel GetMainLayout() private TableLayoutPanel GetMainLayout()
{ {
// Initialize the main layout panel // Initialize the main layout panel
@@ -197,10 +195,10 @@ namespace ModVersionChecker.ui.forms
}; };
// Add recheck logic here // Add recheck logic here
recheckButton.Click += async (s, e) => recheckButton.Click += (s, e) =>
{ {
recheckButton.Enabled = false; recheckButton.Enabled = false;
await _versionService.CheckAllApps(); _appService.CheckAllApps();
UpdateListView(); UpdateListView();
//OnRecheck.Invoke(this, "User initiated recheck from ConfigForm"); //OnRecheck.Invoke(this, "User initiated recheck from ConfigForm");
recheckButton.Enabled = true; recheckButton.Enabled = true;
@@ -212,7 +210,7 @@ namespace ModVersionChecker.ui.forms
public void UpdateListView() public void UpdateListView()
{ {
_apps = _appsManager.Load(); var _apps = _stateService.GetApps();
_listView.Items.Clear(); _listView.Items.Clear();
foreach (var app in _apps) foreach (var app in _apps)
{ {
@@ -252,10 +250,13 @@ namespace ModVersionChecker.ui.forms
private void DeleteApp(App app) private void DeleteApp(App app)
{ {
var result = MessageBox.Show($"Are you sure you want to delete '{app?.Name ?? ""}'?", "Confirm Delete", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); if (app == null) return;
var result = MessageBox.Show($"Are you sure you want to delete '{app.Name ?? ""}'?", "Confirm Delete", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
if (result == DialogResult.Yes) if (result == DialogResult.Yes)
{ {
_appsManager.Delete((_listView.SelectedItems[0].Tag as App).Id); _appService.DeleteApp((_listView.SelectedItems[0].Tag as App));
_stateService.UpdateApps();
UpdateListView(); UpdateListView();
OnConfigChanged?.Invoke(this, EventArgs.Empty); OnConfigChanged?.Invoke(this, EventArgs.Empty);
} }

View File

@@ -19,13 +19,13 @@ namespace ModVersionChecker.ui.forms
private FlowLayoutPanel _mainPanel; private FlowLayoutPanel _mainPanel;
private Button _saveButton, _cancelButton; private Button _saveButton, _cancelButton;
private readonly IApiService _apiService; private readonly IApiService _apiService;
private readonly IConfigManager _configManager; private readonly IConfigRepository _configManager;
private readonly ITypeManager _typeManager; private readonly ITypeConfigRepository _typeManager;
public TypeConfigForm( public TypeConfigForm(
ITypeManager typeManager, ITypeConfigRepository typeManager,
IApiService apiService, IApiService apiService,
IConfigManager configManager IConfigRepository configManager
) )
{ {
_apiService = apiService ?? throw new ArgumentNullException(nameof(apiService)); _apiService = apiService ?? throw new ArgumentNullException(nameof(apiService));