diff --git a/global.json b/global.json index c358071f0..abe582084 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { "version": "10.0.103", - "rollForward": "latestPatch" + "rollForward": "latestFeature" } } diff --git a/src/UniGetUI.Avalonia/UniGetUI.Avalonia.csproj b/src/UniGetUI.Avalonia/UniGetUI.Avalonia.csproj index 94884fca7..2514a337f 100644 --- a/src/UniGetUI.Avalonia/UniGetUI.Avalonia.csproj +++ b/src/UniGetUI.Avalonia/UniGetUI.Avalonia.csproj @@ -94,7 +94,7 @@ - + diff --git a/src/UniGetUI.Avalonia/ViewModels/MainWindowViewModel.cs b/src/UniGetUI.Avalonia/ViewModels/MainWindowViewModel.cs index 4b3ccba2d..62d59ffaf 100644 --- a/src/UniGetUI.Avalonia/ViewModels/MainWindowViewModel.cs +++ b/src/UniGetUI.Avalonia/ViewModels/MainWindowViewModel.cs @@ -108,13 +108,11 @@ partial void OnGlobalSearchTextChanged(string value) private void SubscribeToPageViewModel(AbstractPackagesPage? page) { - if (_subscribedPageViewModel is not null) - _subscribedPageViewModel.PropertyChanged -= OnPageViewModelPropertyChanged; + _subscribedPageViewModel?.PropertyChanged -= OnPageViewModelPropertyChanged; _subscribedPageViewModel = page?.ViewModel; - if (_subscribedPageViewModel is not null) - _subscribedPageViewModel.PropertyChanged += OnPageViewModelPropertyChanged; + _subscribedPageViewModel?.PropertyChanged += OnPageViewModelPropertyChanged; } private void OnPageViewModelPropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e) diff --git a/src/UniGetUI.Avalonia/Views/Controls/InfoBar.axaml.cs b/src/UniGetUI.Avalonia/Views/Controls/InfoBar.axaml.cs index c62c57fb7..6602b1996 100644 --- a/src/UniGetUI.Avalonia/Views/Controls/InfoBar.axaml.cs +++ b/src/UniGetUI.Avalonia/Views/Controls/InfoBar.axaml.cs @@ -23,16 +23,13 @@ public InfoBar() private void OnDataContextChanged(object? sender, EventArgs e) { - if (_vm is not null) - _vm.PropertyChanged -= OnViewModelPropertyChanged; + _vm?.PropertyChanged -= OnViewModelPropertyChanged; _vm = DataContext as InfoBarViewModel; + _vm?.PropertyChanged += OnViewModelPropertyChanged; if (_vm is not null) - { - _vm.PropertyChanged += OnViewModelPropertyChanged; ApplySeverity(_vm.Severity); - } } private void OnViewModelPropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e) diff --git a/src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/PingetCliHelper.cs b/src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/PingetCliHelper.cs index dd87579b7..3796ed563 100644 --- a/src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/PingetCliHelper.cs +++ b/src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/PingetCliHelper.cs @@ -18,7 +18,6 @@ internal sealed partial class PingetCliHelper : IWinGetManagerHelper private static readonly JsonSerializerOptions SerializationOptions = new() { PropertyNameCaseInsensitive = true, - PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower, }; private static readonly PingetCliJsonContext SerializationContext = new(SerializationOptions); @@ -55,7 +54,7 @@ public IReadOnlyList GetAvailableUpdates_UnSafe() match.Id, match.InstalledVersion, match.AvailableVersion!, - GetSource(match.SourceName, match.Id), + GetSource(match), Manager ); @@ -87,7 +86,7 @@ public IReadOnlyList GetInstalledPackages_UnSafe() match.Name, match.Id, match.InstalledVersion, - GetSource(match.SourceName, match.Id), + GetSource(match), Manager ) ) @@ -234,6 +233,32 @@ internal static T DeserializeJson(string output) : throw new InvalidOperationException("Pinget returned empty JSON output."); } + internal static string? InferSourceName(ListMatch match) + { + if (!string.IsNullOrWhiteSpace(match.SourceName)) + { + return match.SourceName; + } + + if (match.Id.Contains("_Microsoft.Winget.Source_8wekyb3d8bbwe", StringComparison.OrdinalIgnoreCase)) + { + return "winget"; + } + + if (!string.IsNullOrWhiteSpace(match.InstallLocation) + && match.InstallLocation.Contains("\\Microsoft\\WinGet\\Packages\\", StringComparison.OrdinalIgnoreCase)) + { + return "winget"; + } + + return null; + } + + private IManagerSource GetSource(ListMatch match) + { + return GetSource(InferSourceName(match), match.Id); + } + private IManagerSource GetSource(string? sourceName, string packageId) { if (string.IsNullOrWhiteSpace(sourceName)) diff --git a/src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/PingetPackageDetailsProvider.cs b/src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/PingetPackageDetailsProvider.cs index 44205ba2b..0f9804384 100644 --- a/src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/PingetPackageDetailsProvider.cs +++ b/src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/PingetPackageDetailsProvider.cs @@ -84,10 +84,10 @@ private IReadOnlyList GetSourceNames(INativeTaskLogger logger) { try { - string output = RunPinget(["source", "list", "--output", "json"], logger); + string output = RunPinget(["source", "export", "--output", "json"], logger); using JsonDocument document = JsonDocument.Parse(output); if ( - !document.RootElement.TryGetProperty("sources", out JsonElement sources) + !document.RootElement.TryGetProperty("Sources", out JsonElement sources) || sources.ValueKind != JsonValueKind.Array ) { @@ -97,7 +97,7 @@ private IReadOnlyList GetSourceNames(INativeTaskLogger logger) return sources .EnumerateArray() .Select(source => - source.TryGetProperty("name", out JsonElement name) + source.TryGetProperty("Name", out JsonElement name) ? name.GetString() : null ) diff --git a/src/UniGetUI.PackageEngine.Managers.WinGet/UniGetUI.PackageEngine.Managers.WinGet.csproj b/src/UniGetUI.PackageEngine.Managers.WinGet/UniGetUI.PackageEngine.Managers.WinGet.csproj index dba42cc45..76720dd8c 100644 --- a/src/UniGetUI.PackageEngine.Managers.WinGet/UniGetUI.PackageEngine.Managers.WinGet.csproj +++ b/src/UniGetUI.PackageEngine.Managers.WinGet/UniGetUI.PackageEngine.Managers.WinGet.csproj @@ -24,7 +24,7 @@ - + diff --git a/src/UniGetUI.PackageEngine.Tests/WinGetManagerTests.cs b/src/UniGetUI.PackageEngine.Tests/WinGetManagerTests.cs index aeafbbe82..de73fd0b5 100644 --- a/src/UniGetUI.PackageEngine.Tests/WinGetManagerTests.cs +++ b/src/UniGetUI.PackageEngine.Tests/WinGetManagerTests.cs @@ -300,28 +300,28 @@ public void FindCandidateExecutableFilesReturnsEmptyWhenNoCliToolExists() [Fact] public void PingetCliHelperDeserializesListResponsesWithGeneratedContext() { - // pinget 0.4.1+ emits snake_case keys. + // Pinget emits PascalCase keys. const string json = """ { - "matches": [ + "Matches": [ { - "name": "Contoso Tool", - "id": "Contoso.Tool", - "local_id": null, - "installed_version": "1.2.3", - "available_version": "2.0.0", - "source_name": "winget", - "publisher": null, - "scope": null, - "installer_category": null, - "install_location": null, - "package_family_names": [], - "product_codes": [], - "upgrade_codes": [] + "Name": "Contoso Tool", + "Id": "Contoso.Tool", + "LocalId": null, + "InstalledVersion": "1.2.3", + "AvailableVersion": "2.0.0", + "SourceName": "winget", + "Publisher": null, + "Scope": null, + "InstallerCategory": null, + "InstallLocation": null, + "PackageFamilyNames": [], + "ProductCodes": [], + "UpgradeCodes": [] } ], - "warnings": [], - "truncated": false + "Warnings": [], + "Truncated": false } """; @@ -335,6 +335,38 @@ public void PingetCliHelperDeserializesListResponsesWithGeneratedContext() Assert.Equal("winget", match.SourceName); } + [Fact] + public void PingetCliHelperInfersWingetSourceWhenInstalledSourceNameIsMissing() + { + const string json = """ + { + "Matches": [ + { + "Name": "Contoso Tool", + "Id": "ARP\\User\\X64\\Contoso.Tool_Microsoft.Winget.Source_8wekyb3d8bbwe", + "LocalId": null, + "InstalledVersion": "1.2.3", + "AvailableVersion": null, + "SourceName": null, + "Publisher": "Contoso", + "Scope": "User", + "InstallerCategory": "exe", + "InstallLocation": "C:\\Users\\example\\AppData\\Local\\Microsoft\\WinGet\\Packages\\Contoso.Tool_Microsoft.Winget.Source_8wekyb3d8bbwe", + "PackageFamilyNames": [], + "ProductCodes": [], + "UpgradeCodes": [] + } + ], + "Warnings": [], + "Truncated": false + } + """; + + ListMatch match = Assert.Single(PingetCliHelper.DeserializeJson(json).Matches); + + Assert.Equal("winget", PingetCliHelper.InferSourceName(match)); + } + [Fact] public void GetCliToolPreferenceUsesEnvironmentBeforeSettings() { diff --git a/src/UniGetUI/Controls/PackageItemContainer.cs b/src/UniGetUI/Controls/PackageItemContainer.cs index 485cc662f..5d440ab1c 100644 --- a/src/UniGetUI/Controls/PackageItemContainer.cs +++ b/src/UniGetUI/Controls/PackageItemContainer.cs @@ -17,15 +17,9 @@ public PackageWrapper Wrapper get => _wrapper; set { - if (_wrapper != null) - { - _wrapper.PropertyChanged -= Wrapper_PropertyChanged; - } + _wrapper?.PropertyChanged -= Wrapper_PropertyChanged; _wrapper = value; - if (_wrapper != null) - { - _wrapper.PropertyChanged += Wrapper_PropertyChanged; - } + _wrapper?.PropertyChanged += Wrapper_PropertyChanged; } } diff --git a/src/UniGetUI/UniGetUI.csproj b/src/UniGetUI/UniGetUI.csproj index 917c3844e..0a25ced00 100644 --- a/src/UniGetUI/UniGetUI.csproj +++ b/src/UniGetUI/UniGetUI.csproj @@ -221,7 +221,7 @@ - +