From fa50323c8898f6eb00e6e050aad8a4f8c0f6c87c Mon Sep 17 00:00:00 2001 From: Gabriel Dufresne Date: Wed, 9 Sep 2026 15:37:47 -0400 Subject: [PATCH] Allow updating a package from the installed packages list --- src/Languages/lang_en.json | 1 - .../SoftwarePages/InstalledPackagesPage.cs | 48 +++++++++++++++++- .../SoftwarePages/SoftwareUpdatesPage.cs | 50 +------------------ 3 files changed, 48 insertions(+), 51 deletions(-) diff --git a/src/Languages/lang_en.json b/src/Languages/lang_en.json index c2c391ceac..61d08074a9 100644 --- a/src/Languages/lang_en.json +++ b/src/Languages/lang_en.json @@ -1010,7 +1010,6 @@ "Manage the list": "Manage the list", "Automatically update selected packages": "Automatically update selected packages", "Manage automatic updates": "Manage automatic updates", - "Update this package automatically": "Update this package automatically", "{0} package(s) marked for automatic updates": "{0} package(s) marked for automatic updates", "Turn on \"Install available updates\" in the scheduled maintenance settings for this to take effect.": "Turn on \"Install available updates\" in the scheduled maintenance settings for this to take effect.", "Every upgradable package is already installed automatically, so this changes nothing until the scheduled task is limited to marked packages.": "Every upgradable package is already installed automatically, so this changes nothing until the scheduled task is limited to marked packages.", diff --git a/src/UniGetUI.Avalonia/Views/SoftwarePages/InstalledPackagesPage.cs b/src/UniGetUI.Avalonia/Views/SoftwarePages/InstalledPackagesPage.cs index 52e4264051..6a7d380950 100644 --- a/src/UniGetUI.Avalonia/Views/SoftwarePages/InstalledPackagesPage.cs +++ b/src/UniGetUI.Avalonia/Views/SoftwarePages/InstalledPackagesPage.cs @@ -34,6 +34,8 @@ public class InstalledPackagesPage : AbstractPackagesPage private MenuItem? _menuOpenInstallLocation; private MenuItem? _menuDownloadInstaller; private MenuItem? _menuManual; + private MenuItem? _menuUpdate; + private MenuItem? _menuUpdateAsAdmin; private static bool _hasBackedUp; @@ -183,6 +185,21 @@ protected override void GenerateToolBar(PackagesPageViewModel vm) }; _menuRemoveData.Click += (_, _) => _ = LaunchUninstall([SelectedItem!], remove_data: true); + _menuUpdate = new MenuItem + { + Header = CoreTools.Translate("Update"), + Icon = LoadMenuIcon("update"), + }; + _menuUpdate.Click += (_, _) => _ = LaunchUpdate(SelectedItem); + + _menuUpdateAsAdmin = new MenuItem + { + Header = CoreTools.Translate("Update as administrator"), + Icon = LoadMenuIcon("uac"), + IsVisible = OperatingSystem.IsWindows(), + }; + _menuUpdateAsAdmin.Click += (_, _) => _ = LaunchUpdate(SelectedItem, elevated: true); + _menuDownloadInstaller = new MenuItem { Header = CoreTools.Translate("Download installer"), @@ -230,6 +247,9 @@ protected override void GenerateToolBar(PackagesPageViewModel vm) menu.Items.Add(_menuInteractive); menu.Items.Add(_menuRemoveData); menu.Items.Add(new Separator()); + menu.Items.Add(_menuUpdate); + menu.Items.Add(_menuUpdateAsAdmin); + menu.Items.Add(new Separator()); menu.Items.Add(_menuDownloadInstaller); menu.Items.Add(new Separator()); menu.Items.Add(_menuReinstall); @@ -244,7 +264,8 @@ protected override void GenerateToolBar(PackagesPageViewModel vm) protected override void WhenShowingContextMenu(IPackage package) { - if (_menuAsAdmin is null || _menuInteractive is null || _menuRemoveData is null + if (_menuUpdate is null || _menuUpdateAsAdmin is null + || _menuAsAdmin is null || _menuInteractive is null || _menuRemoveData is null || _menuInstallationOptions is null || _menuReinstall is null || _menuUninstallThenReinstall is null || _menuIgnoreUpdates is null || _menuDetails is null @@ -262,6 +283,17 @@ protected override void WhenShowingContextMenu(IPackage package) _menuAsAdmin.IsEnabled = caps.CanRunAsAdmin; _menuInteractive.IsEnabled = caps.CanRunInteractively; _menuRemoveData.IsEnabled = caps.CanRemoveDataOnUninstall; + + // The installed entry knows no target version; its upgradable counterpart does, + // and is null whenever no update is pending for the package. + var upgradable = package.GetUpgradablePackage(); + bool canUpdate = upgradable is not null; + _menuUpdate.IsEnabled = canUpdate; + _menuUpdate.Header = upgradable is null + ? CoreTools.Translate("Update") + : CoreTools.Translate("Update to version {0}", upgradable.NewVersionString); + _menuUpdateAsAdmin.IsEnabled = canUpdate && caps.CanRunAsAdmin; + _menuDownloadInstaller.IsEnabled = !isLocal && caps.CanDownloadInstaller; _menuInstallationOptions.IsEnabled = !isLocal; _menuReinstall.IsEnabled = !isLocal; @@ -373,6 +405,20 @@ private static async Task LaunchUninstall( } } + private static async Task LaunchUpdate(IPackage? package, bool? elevated = null) + { + // Updates must run on the upgradable instance: the installed one reports + // NewVersionString == VersionString, which managers read as "nothing to do". + if (package?.GetUpgradablePackage() is not { } upgradable) return; + var opts = await InstallOptionsFactory.LoadApplicableAsync(upgradable, elevated: elevated); + if (PackageOperation.HasPendingOperation(upgradable, OperationType.Update)) return; + var op = new UpdatePackageOperation(upgradable, opts); + op.OperationSucceeded += (_, _) => TelemetryHandler.UpdatePackage(upgradable, TEL_OP_RESULT.SUCCESS); + op.OperationFailed += (_, _) => TelemetryHandler.UpdatePackage(upgradable, TEL_OP_RESULT.FAILED); + AvaloniaOperationRegistry.Add(op); + _ = op.MainThread(); + } + private static async Task LaunchReinstall(IPackage? package) { if (package is null || package.Source.IsVirtualManager) return; diff --git a/src/UniGetUI.Avalonia/Views/SoftwarePages/SoftwareUpdatesPage.cs b/src/UniGetUI.Avalonia/Views/SoftwarePages/SoftwareUpdatesPage.cs index 0f827be4b9..c3473b484a 100644 --- a/src/UniGetUI.Avalonia/Views/SoftwarePages/SoftwareUpdatesPage.cs +++ b/src/UniGetUI.Avalonia/Views/SoftwarePages/SoftwareUpdatesPage.cs @@ -28,7 +28,6 @@ public class SoftwareUpdatesPage : AbstractPackagesPage private MenuItem? _menuSkipHash; private MenuItem? _menuDownloadInstaller; private MenuItem? _menuOpenInstallLocation; - private MenuItem? _menuAutoUpdate; public SoftwareUpdatesPage() : base(new PackagesPageData { @@ -198,23 +197,6 @@ protected override void GenerateToolBar(PackagesPageViewModel vm) UpgradablePackagesLoader.Instance.IgnoredPackages[pkg.Id] = pkg; }; - _menuAutoUpdate = new MenuItem - { - Header = CoreTools.Translate("Update this package automatically"), - Icon = LoadMenuIcon("sandclock"), - ToggleType = MenuItemToggleType.CheckBox, - }; - _menuAutoUpdate.Click += (_, _) => - { - var pkg = SelectedItem; - if (pkg is null) return; - string id = AutoUpdatesDatabase.GetIdForPackage(pkg); - if (AutoUpdatesDatabase.IsAutoUpdated(id)) - AutoUpdatesDatabase.Remove(id); - else - MarkForAutoUpdates([pkg]); - }; - var menuSkipVersion = new MenuItem { Header = CoreTools.Translate("Skip this version"), @@ -282,8 +264,6 @@ protected override void GenerateToolBar(PackagesPageViewModel vm) menu.Items.Add(menuUninstallThenUpdate); menu.Items.Add(menuUninstall); menu.Items.Add(new Separator()); - menu.Items.Add(_menuAutoUpdate); - menu.Items.Add(new Separator()); menu.Items.Add(menuIgnore); menu.Items.Add(menuSkipVersion); menu.Items.Add(menuPause); @@ -296,8 +276,7 @@ protected override void GenerateToolBar(PackagesPageViewModel vm) protected override void WhenShowingContextMenu(IPackage package) { if (_menuAsAdmin is null || _menuInteractive is null || _menuSkipHash is null - || _menuDownloadInstaller is null || _menuOpenInstallLocation is null - || _menuAutoUpdate is null) + || _menuDownloadInstaller is null || _menuOpenInstallLocation is null) { Logger.Warn("Context menu items are null on SoftwareUpdatesPage"); return; @@ -310,7 +289,6 @@ protected override void WhenShowingContextMenu(IPackage package) _menuDownloadInstaller.IsEnabled = caps.CanDownloadInstaller; _menuOpenInstallLocation.IsEnabled = package.Manager.DetailsHelper.GetInstallLocation(package) is not null; - _menuAutoUpdate.IsChecked = AutoUpdatesDatabase.IsAutoUpdated(package); } // ─── Abstract action overrides ──────────────────────────────────────────── @@ -461,32 +439,6 @@ private static async Task WhenPackagesLoaded() } } - private static void MarkForAutoUpdates(IEnumerable packages) - { - int marked = 0; - foreach (var pkg in packages) - { - string id = AutoUpdatesDatabase.GetIdForPackage(pkg); - if (AutoUpdatesDatabase.IsAutoUpdated(id)) continue; - AutoUpdatesDatabase.Add(id); - marked++; - } - - if (marked is 0) return; - - var schedule = MaintenanceScheduleStore.Get(MaintenanceTaskKind.InstallUpdates); - string message = !schedule.Enabled - ? CoreTools.Translate("Turn on \"Install available updates\" in the scheduled maintenance settings for this to take effect.") - : schedule.InstallTargets is ScheduleInstallTargets.AllPackages - ? CoreTools.Translate("Every upgradable package is already installed automatically, so this changes nothing until the scheduled task is limited to marked packages.") - : CoreTools.Translate("They will be updated when the scheduled maintenance task runs."); - - GetMainWindow()?.ShowBanner( - CoreTools.Translate("{0} package(s) marked for automatic updates", marked), - message, - MainWindow.RuntimeNotificationLevel.Success); - } - private static async Task LaunchScheduledUpdate(IReadOnlyList upgradable) { bool markedOnly = MaintenanceScheduleStore.GetInstallTargets()