Skip to content

Commit

Permalink
implement package updater system
Browse files Browse the repository at this point in the history
  • Loading branch information
HendrikMennen committed Nov 25, 2023
1 parent 029a544 commit 92a37fc
Show file tree
Hide file tree
Showing 10 changed files with 370 additions and 187 deletions.
2 changes: 2 additions & 0 deletions src/OneWare.Core/Styles/Accents/Base.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
<Color x:Key="HighlightForegroundColor">#FFFFFFFF</Color>
<Color x:Key="ErrorColor">#FFFF0000</Color>
<Color x:Key="ErrorLowColor">#10FF0000</Color>
<Color x:Key="WarningColor">#FFFF00</Color>
<SolidColorBrush x:Key="HighlightForegroundBrush" Color="{StaticResource HighlightForegroundColor}" />
<SolidColorBrush x:Key="ThemeForegroundLowBrush" Color="{StaticResource ThemeForegroundLowColor}" />
<SolidColorBrush x:Key="ThemeAccentBrush" Color="{DynamicResource ThemeAccentColor}" />
Expand All @@ -30,6 +31,7 @@
<SolidColorBrush x:Key="ThemeAccentBrush4" Color="{DynamicResource ThemeAccentColor}" Opacity="0.3" />
<SolidColorBrush x:Key="ErrorBrush" Color="{StaticResource ErrorColor}" />
<SolidColorBrush x:Key="ErrorLowBrush" Color="{StaticResource ErrorLowColor}" />
<SolidColorBrush x:Key="WarningBrush" Color="{StaticResource WarningColor}"></SolidColorBrush>
<SolidColorBrush
x:Key="NotificationCardBackgroundBrush"
Opacity="0.9"
Expand Down
1 change: 1 addition & 0 deletions src/OneWare.PackageManager/Enums/PackageStatus.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ public enum PackageStatus
UpdateAvailable,
Installing,
Installed,
NeedRestart
}

This file was deleted.

22 changes: 22 additions & 0 deletions src/OneWare.PackageManager/Serializer/InstalledPackage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace OneWare.PackageManager.Serializer;

public class InstalledPackage(
string id,
string type,
string? category,
string? description,
string? license,
string installedVersion)
{
public string Id { get; } = id;

public string Type { get; } = type;

public string? Category { get; } = category;

public string? Description { get; } = description;

public string? License { get; } = license;

public string InstalledVersion { get; } = installedVersion;
}
157 changes: 97 additions & 60 deletions src/OneWare.PackageManager/ViewModels/PackageManagerViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,23 @@
using Avalonia;
using Avalonia.Controls;
using CommunityToolkit.Mvvm.ComponentModel;
using DynamicData;
using OneWare.PackageManager.Enums;
using OneWare.PackageManager.Models;
using OneWare.PackageManager.Serializer;
using OneWare.PackageManager.Serializer.Installed;
using OneWare.SDK.Services;
using Prism.Ioc;

namespace OneWare.PackageManager.ViewModels;

public class PackageManagerViewModel : ObservableObject
{
private static readonly JsonSerializerOptions SerializerOptions = new()
{
PropertyNameCaseInsensitive = true,
AllowTrailingCommas = true,
WriteIndented = true
};

private readonly IHttpService _httpService;
private readonly ILogger _logger;
private PackageCategoryModel? _selectedCategory;
Expand Down Expand Up @@ -71,46 +76,17 @@ public PackageCategoryModel? SelectedCategory
set => SetProperty(ref _selectedCategory, value);
}

public ObservableCollection<PackageCategoryModel> PackageCategories { get; } = new();
public ObservableCollection<PackageCategoryModel> PackageCategories { get; } = [];

public Dictionary<string, PackageViewModel> Packages { get; } = new();

public PackageManagerViewModel(IHttpService httpService, ILogger logger, IPaths paths)
{
_httpService = httpService;
_logger = logger;

PackageDataBasePath = Path.Combine(paths.PackagesDirectory, "oneware-packages.json");
_ = LoadPackagesAsync();
}

public void RegisterPackageCategory(PackageCategoryModel category)
{
PackageCategories.Add(category);
}

public void RegisterPackage(PackageCategoryModel category, PackageViewModel packageView)
{
PackageCategories.First().Packages.Add(packageView);
category.Packages.Add(packageView);

Observable.FromEventPattern(packageView, nameof(packageView.Installed))
.Subscribe(x => _ = SaveInstalledPackagesDatabaseAsync())
.DisposeWith(_packageRegistrationSubscription);

Observable.FromEventPattern(packageView, nameof(packageView.Removed))
.Subscribe(x => _ = SaveInstalledPackagesDatabaseAsync())
.DisposeWith(_packageRegistrationSubscription);
}

public async Task LoadPackagesAsync()
{
if(_cancellationTokenSource is not null) await _cancellationTokenSource.CancelAsync();
_cancellationTokenSource = new CancellationTokenSource();

_packageRegistrationSubscription?.Dispose();
_packageRegistrationSubscription = new CompositeDisposable();

IsLoading = true;
PackageCategories.Clear();
PackageCategories.Add(new PackageCategoryModel("All"));
SelectedCategory = PackageCategories.First();

Expand All @@ -121,45 +97,91 @@ public async Task LoadPackagesAsync()
RegisterPackageCategory(new PackageCategoryModel("Libraries", Application.Current?.GetResourceObservable("BoxIcons.RegularLibrary")));
RegisterPackageCategory(new PackageCategoryModel("Misc", Application.Current?.GetResourceObservable("Module")));

await LoadPackageRepositoryAsync(
"https://raw.githubusercontent.com/ProtopSolutions/OneWare.PublicPackages/main/oneware-packages.json", _cancellationTokenSource.Token);

FilterPackages();
IsLoading = false;
_ = LoadPackagesAsync();
}

public void FilterPackages()
private void FilterPackages()
{
foreach (var cat in PackageCategories)
{
cat.Filter(Filter, _showInstalled, _showAvailable);
}
}

private readonly JsonSerializerOptions _serializerOptions = new()
private void RegisterPackageCategory(PackageCategoryModel category)
{
PropertyNameCaseInsensitive = true,
AllowTrailingCommas = true,
};

private void AddPackage(Package package)
PackageCategories.Add(category);
}

private void RegisterPackage(Package package, string? installedVersion = null)
{
if (package.Id == null) throw new Exception("Package ID cannot be empty");

if (Packages.TryGetValue(package.Id, out var existing))
{
existing.Package = package;
return;
}

var model = package.Type switch
{
"Plugin" => ContainerLocator.Container.Resolve<PluginPackageViewModel>((typeof(Package), package)),
_ => null
_ => throw new Exception($"Package Type invalid/missing for {package.Name}!")
};

if (model == null)
if (installedVersion != null)
{
throw new Exception($"Package Type invalid/missing for {package.Name}!");
model.InstalledVersion = package.Versions!.First(x => x.Version == installedVersion);
model.Status = PackageStatus.Installed;
}
else
{
model.Status = PackageStatus.Available;
}

var category = PackageCategories.FirstOrDefault(x =>
x.Header.Equals(package.Category, StringComparison.OrdinalIgnoreCase)) ?? PackageCategories.Last();
x.Header.Equals(package.Category, StringComparison.OrdinalIgnoreCase)) ?? PackageCategories.Last();

PackageCategories.First().Packages.Add(model);
Packages.Add(package.Id, model);
category.Packages.Add(model);

Observable.FromEventPattern(model, nameof(model.Installed))
.Subscribe(x => _ = SaveInstalledPackagesDatabaseAsync())
.DisposeWith(_packageRegistrationSubscription);

Observable.FromEventPattern(model, nameof(model.Removed))
.Subscribe(x => _ = SaveInstalledPackagesDatabaseAsync())
.DisposeWith(_packageRegistrationSubscription);
}

public async Task LoadPackagesAsync()
{
if(_cancellationTokenSource is not null) await _cancellationTokenSource.CancelAsync();
_cancellationTokenSource = new CancellationTokenSource();

_packageRegistrationSubscription?.Dispose();
_packageRegistrationSubscription = new CompositeDisposable();

RegisterPackage(category, model);
IsLoading = true;
var removals = Packages.Where(x => x.Value.Status is PackageStatus.Available);
foreach (var rm in removals)
{
Packages.Remove(rm.Key);
foreach (var category in PackageCategories)
{
category.Packages.Remove(rm.Value);
category.VisiblePackages.Remove(rm.Value);
}
}

await LoadInstalledPackagesDatabaseAsync(_cancellationTokenSource.Token);

await LoadPackageRepositoryAsync(
"https://raw.githubusercontent.com/ProtopSolutions/OneWare.PublicPackages/main/oneware-packages.json", _cancellationTokenSource.Token);

FilterPackages();
IsLoading = false;
}

private async Task LoadPackageRepositoryAsync(string url, CancellationToken cancellationToken)
Expand All @@ -170,7 +192,7 @@ private async Task LoadPackageRepositoryAsync(string url, CancellationToken canc
{
if (repositoryString == null) throw new NullReferenceException(nameof(repositoryString));

var repository = JsonSerializer.Deserialize<PackageRepository>(repositoryString, _serializerOptions);
var repository = JsonSerializer.Deserialize<PackageRepository>(repositoryString, SerializerOptions);

if (repository is { Packages: not null })
{
Expand All @@ -182,11 +204,11 @@ private async Task LoadPackageRepositoryAsync(string url, CancellationToken canc
await _httpService.DownloadTextAsync(manifest.ManifestUrl,
cancellationToken: cancellationToken);

var package = JsonSerializer.Deserialize<Package>(downloadManifest!, _serializerOptions);
var package = JsonSerializer.Deserialize<Package>(downloadManifest!, SerializerOptions);

if(package == null) continue;

AddPackage(package);
RegisterPackage(package);
}
}
else throw new Exception("Packages empty");
Expand All @@ -203,13 +225,28 @@ private async Task LoadInstalledPackagesDatabaseAsync(CancellationToken token)
{
if (File.Exists(PackageDataBasePath))
{
await using var file = File.OpenWrite(PackageDataBasePath);
var installedPackages = await JsonSerializer.DeserializeAsync<InstalledPackage[]>(file, cancellationToken: token);
await using var file = File.OpenRead(PackageDataBasePath);
var installedPackages = await JsonSerializer.DeserializeAsync<InstalledPackage[]>(file, SerializerOptions, token);
if (installedPackages != null)
{
foreach (var installedPackage in installedPackages)
{
AddPackage(installedPackage.Package);
var package = new Package()
{
Id = installedPackage.Id,
Type = installedPackage.Type,
Category = installedPackage.Category,
Versions = [
new PackageVersion()
{
Version = installedPackage.InstalledVersion
}
],
Description = installedPackage.Description,
License = installedPackage.License
};

RegisterPackage(package, installedPackage.InstalledVersion);
}
}
}
Expand All @@ -229,10 +266,10 @@ private async Task SaveInstalledPackagesDatabaseAsync()

var installedPackages = PackageCategories.First().Packages
.Where(x => x.Status == PackageStatus.Installed)
.Select(x => new InstalledPackage(x.Package, x.InstalledVersion!.Version!))
.Select(x => new InstalledPackage(x.Package.Id!, x.Package.Type!, x.Package.Category, x.Package.Description, x.Package.License, x.InstalledVersion!.Version!))
.ToArray();

await JsonSerializer.SerializeAsync<InstalledPackage[]>(file, installedPackages);
await JsonSerializer.SerializeAsync(file, installedPackages, SerializerOptions);
}
catch (Exception e)
{
Expand Down
Loading

0 comments on commit 92a37fc

Please sign in to comment.