Skip to content

Commit

Permalink
Merge pull request #1 from dragonfruitnetwork/net8-mvvm
Browse files Browse the repository at this point in the history
Upgrade to .NET 8 (and re-write some MVMM components)
  • Loading branch information
aspriddell authored Jul 10, 2024
2 parents 13bdb39 + 976c727 commit 9b81609
Show file tree
Hide file tree
Showing 16 changed files with 387 additions and 276 deletions.
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 --severity=WARNING --properties:Configuration=Release

- 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;
private readonly PackageManager _manager;
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)
{
_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

0 comments on commit 9b81609

Please sign in to comment.