Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade to .NET 8 (and re-write some MVMM components) #1

Merged
merged 15 commits into from
Jul 10, 2024
Merged
8 changes: 4 additions & 4 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ jobs:
arch: [win10-x64, win10-arm64]

steps:
- uses: actions/checkout@v3
- uses: microsoft/setup-msbuild@v1.1
- uses: actions/setup-dotnet@v2
- uses: actions/checkout@v4
- uses: microsoft/setup-msbuild@v2
- uses: actions/setup-dotnet@v4
with:
dotnet-version: |
6.0.x
7.0.x
8.0.x

- name: Setup NuGet
run: dotnet nuget add source --username github --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/dragonfruitnetwork/index.json"
Expand Down
18 changes: 9 additions & 9 deletions .github/workflows/quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,21 @@ jobs:
quality:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-dotnet@v2
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: |
"6.0.x"
"7.0.x"
dotnet-version: '8.0.x'

- name: Restore Project/Tools
run: |
dotnet restore
dotnet tool install --global NVika
dotnet tool install --global JetBrains.ReSharper.GlobalTools

- name: InspectCode
run: jb inspectcode DragonFruit.Kaplan.sln --output=inspectcodereport.xml --verbosity=WARN --no-build
run: jb inspectcode DragonFruit.Kaplan.sln --output=inspectcode.sarif --verbosity=WARN --no-build

- name: Vika
run: nvika parsereport "${{github.workspace}}/inspectcodereport.xml"
- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: inspectcode.sarif
category: InspectCode
1 change: 1 addition & 0 deletions DragonFruit.Kaplan.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ Licensed under Apache-2. Refer to the LICENSE file for more info</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=VirtualMemberNeverOverridden_002ELocal/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=WrongIndentSize/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=CA/@EntryIndexedValue">CA</s:String>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EFeature_002EServices_002ECodeCleanup_002EFileHeader_002EFileHeaderSettingsMigrate/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
Expand Down
2 changes: 1 addition & 1 deletion DragonFruit.Kaplan/App.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public override void Initialize()
s.MaxBreadcrumbs = 200;
s.MinimumEventLevel = LogLevel.Warning;

s.SetBeforeSend(e => BugReportingEnabled ? e : null);
s.SetBeforeSend(e => BugReportingEnabled && typeof(Program).Assembly.GetName().Version?.Major > 1 ? e : null);
});
});

Expand Down
19 changes: 9 additions & 10 deletions DragonFruit.Kaplan/DragonFruit.Kaplan.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<ApplicationIcon>Assets\icon.ico</ApplicationIcon>
<Configurations>Debug;Release;DryRun</Configurations>
<ApplicationManifest>app.manifest</ApplicationManifest>
<TargetFramework>net7.0-windows10.0.19041</TargetFramework>
<TargetFramework>net8.0-windows10.0.19041</TargetFramework>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>

<DefineConstants Condition="'$(Configuration)' != 'Release'">$(Constants);DRY_RUN</DefineConstants>
Expand Down Expand Up @@ -35,17 +35,16 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Avalonia" Version="11.0.0" />
<PackageReference Include="Avalonia.Desktop" Version="11.0.0" />
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.0" />
<PackageReference Include="FluentAvaloniaUI" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.EventLog" Version="7.0.0" />
<PackageReference Include="Nito.AsyncEx.Coordination" Version="5.1.2" />
<PackageReference Include="Sentry.Extensions.Logging" Version="3.34.0" />
<PackageReference Include="Avalonia" Version="11.0.11" />
<PackageReference Include="Avalonia.Desktop" Version="11.0.11" />
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.11" />
<PackageReference Include="FluentAvaloniaUI" Version="2.0.5" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.EventLog" Version="8.0.0" />
<PackageReference Include="Sentry.Extensions.Logging" Version="4.8.1" />

<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.0" />
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.11" />
</ItemGroup>

</Project>
159 changes: 159 additions & 0 deletions DragonFruit.Kaplan/PackageRemover.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
// Kaplan Copyright (c) DragonFruit Network <[email protected]>
// Licensed under Apache-2. Refer to the LICENSE file for more info

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Windows.ApplicationModel;
using Windows.Management.Deployment;
using DragonFruit.Kaplan.ViewModels.Enums;

namespace DragonFruit.Kaplan
{
public class PackageRemover
{
private readonly RemovalOptions _mode;
Fixed Show fixed Hide fixed
private readonly PackageManager _manager;
Fixed Show fixed Hide fixed
private readonly IReadOnlyList<Package> _packages;

private Package _currentPackage;
private DeploymentProgress _currentPackageRemovalProgress;

private OperationState _state;
private Task<int> _currentRemovalTask;

public PackageRemover(PackageInstallationMode mode, PackageManager manager, IReadOnlyList<Package> packages)
Fixed Show fixed Hide fixed
{
_manager = manager;
_packages = packages;

_mode = mode switch
{
PackageInstallationMode.Machine => RemovalOptions.RemoveForAllUsers,
PackageInstallationMode.User => RemovalOptions.None,

_ => throw new ArgumentOutOfRangeException()
};
}

/// <summary>
/// The total number of packages to be removed
/// </summary>
public int TotalPackages => _packages.Count;

/// <summary>
/// The index of the package currently being removed
/// </summary>
public int CurrentIndex { get; private set; }

public OperationState State
{
get => _state;
private set
{
if (_state == value) return;

_state = value;
StateChanged?.Invoke(this, value);
}
}

public Package CurrentPackage
{
get => _currentPackage;
private set
{
_currentPackage = value;
CurrentPackageChanged?.Invoke(this, value);
}
}

public DeploymentProgress CurrentPackageRemovalProgress
{
get => _currentPackageRemovalProgress;
private set
{
_currentPackageRemovalProgress = value;
CurrentPackageRemovalProgressChanged?.Invoke(this, value);
}
}

public event EventHandler<OperationState> StateChanged;
public event EventHandler<Package> CurrentPackageChanged;
public event EventHandler<DeploymentProgress> CurrentPackageRemovalProgressChanged;

/// <summary>
/// Iterates through the provided packages, removing them from the system.
/// If previously cancelled, will continue from the last package.
/// </summary>
public Task<int> RemovePackagesAsync(CancellationToken cancellation = default)
{
// prevent multiple removal tasks from running at once
if (_currentRemovalTask?.Status is TaskStatus.WaitingForActivation or TaskStatus.WaitingToRun or TaskStatus.Running or TaskStatus.Created)
{
return _currentRemovalTask;
}

return _currentRemovalTask = RemovePackagesAsyncImpl(cancellation);
}

private async Task<int> RemovePackagesAsyncImpl(CancellationToken cancellation)
{
var removed = 0;
State = OperationState.Pending;

for (int i = CurrentIndex; i < _packages.Count; i++)
{
if (cancellation.IsCancellationRequested)
{
State = OperationState.Canceled;
break;
}

CurrentIndex = i;
CurrentPackage = _packages[i];
CurrentPackageRemovalProgress = default;

try
{
State = OperationState.Running;
#if !DRY_RUN
var progress = new Progress<DeploymentProgress>(p => CurrentPackageRemovalProgress = p);
await _manager.RemovePackageAsync(_packages[i].Id.FullName, _mode).AsTask(cancellation, progress).ConfigureAwait(false);
#else
// dummy removal progress
for (uint j = 0; j < 50; j++)
{
await Task.Delay(50, cancellation);
CurrentPackageRemovalProgress = new DeploymentProgress(DeploymentProgressState.Processing, j * 2);
}
#endif
removed++;
}
catch (OperationCanceledException)
{
State = OperationState.Canceled;
return removed;
}
catch
{
State = OperationState.Errored;
return removed;
}
}

State = OperationState.Completed;
return removed;
}

public enum OperationState
{
Pending,
Running,
Errored,
Completed,
Canceled
}
}
}
76 changes: 76 additions & 0 deletions DragonFruit.Kaplan/ReactiveAppWindow.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Kaplan Copyright (c) DragonFruit Network <[email protected]>
// Licensed under Apache-2. Refer to the LICENSE file for more info

using Avalonia;
using Avalonia.Controls;
using Avalonia.ReactiveUI;
using FluentAvalonia.UI.Windowing;
using ReactiveUI;

#nullable enable

namespace DragonFruit.Kaplan
{
/// <summary>
/// A ReactiveUI <see cref="Window"/> that implements the <see cref="IViewFor"/> interface and will
/// activate your ViewModel automatically if the view model implements <see cref="IActivatableViewModel"/>. When
/// the DataContext property changes, this class will update the ViewModel property with the new DataContext value,
/// and vice versa.
/// </summary>
/// <typeparam name="TViewModel">ViewModel type.</typeparam>
/// <remarks>
/// This is a version of the ReactiveUI <see cref="ReactiveWindow{TViewModel}"/> class modified to support <see cref="AppWindow"/>.
/// See https://github.com/AvaloniaUI/Avalonia/blob/master/src/Avalonia.ReactiveUI/ReactiveWindow.cs for the original implementation.
/// </remarks>
public class ReactiveAppWindow<TViewModel> : AppWindow, IViewFor<TViewModel> where TViewModel : class
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1002", Justification = "Generic avalonia property is expected here.")]
public static readonly StyledProperty<TViewModel?> ViewModelProperty = AvaloniaProperty.Register<ReactiveWindow<TViewModel>, TViewModel?>(nameof(ViewModel));

/// <summary>
/// Initializes a new instance of the <see cref="ReactiveWindow{TViewModel}"/> class.
/// </summary>
public ReactiveAppWindow()
{
// This WhenActivated block calls ViewModel's WhenActivated
// block if the ViewModel implements IActivatableViewModel.
this.WhenActivated(disposables => { });
}

/// <summary>
/// The ViewModel.
/// </summary>
public TViewModel? ViewModel
{
get => GetValue(ViewModelProperty);
set => SetValue(ViewModelProperty, value);
}

object? IViewFor.ViewModel
{
get => ViewModel;
set => ViewModel = (TViewModel?)value;
}

protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);

if (change.Property == DataContextProperty)
{
if (ReferenceEquals(change.OldValue, ViewModel)
&& change.NewValue is null or TViewModel)
{
SetCurrentValue(ViewModelProperty, change.NewValue);
}
}
else if (change.Property == ViewModelProperty)
{
if (ReferenceEquals(change.OldValue, DataContext))
{
SetCurrentValue(DataContextProperty, change.NewValue);
}
}
}
}
}
Loading