diff --git a/src/Directory.Build.props b/src/Directory.Build.props index d4020c2f92..e6fcb59e62 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -3,7 +3,7 @@ enable net8.0-windows10.0.26100.0 10.0.19041.0 - 10.0.26100.57 + 10.0.26100.56 8.0.407 Martí Climent and the contributors Martí Climent diff --git a/src/UniGetUI.PackageEngine.PackageLoader/DiscoverablePackagesLoader.cs b/src/UniGetUI.PackageEngine.PackageLoader/DiscoverablePackagesLoader.cs index 0e61004ef3..50f011c72f 100644 --- a/src/UniGetUI.PackageEngine.PackageLoader/DiscoverablePackagesLoader.cs +++ b/src/UniGetUI.PackageEngine.PackageLoader/DiscoverablePackagesLoader.cs @@ -1,3 +1,4 @@ +using UniGetUI.Core.Logging; using UniGetUI.Core.Tools; using UniGetUI.Interface.Enums; using UniGetUI.PackageEngine.Interfaces; @@ -66,5 +67,46 @@ protected override Task WhenAddingPackage(IPackage package) } return Task.CompletedTask; } + + public (IPackage?, string?) GetPackageFromIdAndManager(string id, string managerName, string sourceName) + { + IPackageManager? manager = null; + + foreach (var candidate in Managers) + { + if (candidate.Name == managerName || candidate.DisplayName == managerName) + { + manager = candidate; + break; + } + } + + if (manager is null) + return (null, CoreTools.Translate("The package manager \"{0}\" was not found", managerName)); + + if (!manager.IsEnabled()) + return (null, CoreTools.Translate("The package manager \"{0}\" is disabled", manager.DisplayName)); + + if (!manager.Status.Found) + return (null, CoreTools.Translate("There is an error with the configuration of the package manager \"{0}\"", manager.DisplayName)); + + var results = manager.FindPackages(id); + var candidates = results.Where(p => p.Id == id).ToArray(); + + if (candidates.Length == 0) + return (null, CoreTools.Translate("The package \"{0}\" was not found on the package manager \"{1}\"", id, manager.DisplayName)); + + IPackage package = candidates[0]; + + // Get package from best source + if (candidates.Length >= 1 && manager.Capabilities.SupportsCustomSources) + foreach (var candidate in candidates) + { + if (candidate.Source.Name == sourceName) + package = candidate; + } + + return (package, null); + } } } diff --git a/src/UniGetUI/App.xaml b/src/UniGetUI/App.xaml index f6ce147d13..6214716716 100644 --- a/src/UniGetUI/App.xaml +++ b/src/UniGetUI/App.xaml @@ -22,6 +22,15 @@ + + /Assets/Symbols/Font/fonts/UniGetUI-Symbols.ttf#UniGetUI-Symbols @@ -34,6 +43,625 @@ + + + + + + diff --git a/src/UniGetUI/App.xaml.cs b/src/UniGetUI/App.xaml.cs index aa641b1b26..f16529aed2 100644 --- a/src/UniGetUI/App.xaml.cs +++ b/src/UniGetUI/App.xaml.cs @@ -18,6 +18,8 @@ using UniGetUI.Interface.Telemetry; using UniGetUI.PackageEngine.Interfaces; using LaunchActivatedEventArgs = Microsoft.UI.Xaml.LaunchActivatedEventArgs; +using UniGetUI.Pages.DialogPages; +using UniGetUI.Interface.Enums; namespace UniGetUI { @@ -227,23 +229,22 @@ private async Task LoadComponentsAsync() BackgroundApi.OnShowSharedPackage += (_, package) => MainWindow.DispatcherQueue.TryEnqueue(() => { - MainWindow?.NavigationPage?.DiscoverPage.ShowSharedPackage_ThreadSafe(package.Key, package.Value); - MainWindow?.Activate(); + DialogHelper.ShowSharedPackage_ThreadSafe(package.Key, package.Value); }); BackgroundApi.OnUpgradeAll += (_, _) => MainWindow.DispatcherQueue.TryEnqueue(() => { - MainWindow?.NavigationPage?.UpdatesPage.UpdateAll(); + Operations.UpdateAll(); }); - BackgroundApi.OnUpgradeAllForManager += (_, manager) => MainWindow.DispatcherQueue.TryEnqueue(() => + BackgroundApi.OnUpgradeAllForManager += (_, managerName) => MainWindow.DispatcherQueue.TryEnqueue(async () => { - MainWindow?.NavigationPage?.UpdatesPage.UpdateAllPackagesForManager(manager); + Operations.UpdateAllForManager(managerName); }); - BackgroundApi.OnUpgradePackage += (_, package) => MainWindow.DispatcherQueue.TryEnqueue(() => + BackgroundApi.OnUpgradePackage += (_, packageId) => MainWindow.DispatcherQueue.TryEnqueue(() => { - MainWindow?.NavigationPage?.UpdatesPage.UpdatePackageForId(package); + Operations.UpdateForId(packageId); }); _ = BackgroundApi.Start(); diff --git a/src/UniGetUI/AppOperationHelper.cs b/src/UniGetUI/AppOperationHelper.cs index 83643ea1dd..9b08b250fc 100644 --- a/src/UniGetUI/AppOperationHelper.cs +++ b/src/UniGetUI/AppOperationHelper.cs @@ -15,6 +15,8 @@ using UniGetUI.PackageEngine.PackageClasses; using UniGetUI.PackageOperations; using UniGetUI.Pages.DialogPages; +using UniGetUI.Interface.Enums; +using UniGetUI.PackageEngine; namespace UniGetUI; @@ -163,6 +165,38 @@ public static void Update(IReadOnlyList packages, bool? elevated = nul } } + public static async void UpdateAll() + { + foreach (IPackage package in PEInterface.UpgradablePackagesLoader.Packages) + if (package.Tag is not PackageTag.BeingProcessed and not PackageTag.OnQueue) + await Update(package); + } + + public static async void UpdateAllForManager(string managerName) + { + foreach (IPackage package in PEInterface.UpgradablePackagesLoader.Packages) + { + if (package.Tag is not PackageTag.OnQueue and not PackageTag.BeingProcessed + && package.Manager.Name == managerName || package.Manager.DisplayName == managerName) + await Update(package); + } + } + + public static async void UpdateForId(string packageId) + { + foreach (IPackage package in PEInterface.UpgradablePackagesLoader.Packages) + { + if (package.Id == packageId) + { + await Update(package); + Logger.Info($"[WIDGETS] Updating package with id {packageId}"); + return; + } + } + + Logger.Warn($"[WIDGETS] No package with id={packageId} was found"); + } + /* * PACKAGE UNINSTALL */ diff --git a/src/UniGetUI/Controls/LocalIcon.cs b/src/UniGetUI/Controls/LocalIcon.cs index f90676c063..9c784aaabc 100644 --- a/src/UniGetUI/Controls/LocalIcon.cs +++ b/src/UniGetUI/Controls/LocalIcon.cs @@ -1,3 +1,4 @@ +using System.Diagnostics; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media; @@ -25,6 +26,38 @@ public LocalIcon(IconType icon) : this() } } + public static class IconBuilder + { + private static FontFamily customFont = null!; + private static FontFamily symbolFont = null!; + + public static IconType SetIcon(this TextBlock block, IconType icon) + { + customFont ??= (FontFamily)Application.Current.Resources["SymbolFont"]; + block.Text = $"{(char)icon}"; + block.FontFamily = customFont; + return icon; + } + + public static IconType GetIcon(this TextBlock block) + { + return IconType.Help; + } + + public static string SetGlyph(this TextBlock block, string glyph) + { + symbolFont ??= new FontFamily("Segoe Fluent Icons"); + block.Text = glyph; + block.FontFamily = symbolFont; + return glyph; + } + + public static string GetGlyph(this TextBlock block) + { + return block.Text; + } + } + public partial class LocalIconSource : FontIconSource { public static FontFamily font = (FontFamily)Application.Current.Resources["SymbolFont"]; diff --git a/src/UniGetUI/Controls/MenuForPackage.cs b/src/UniGetUI/Controls/MenuForPackage.cs index 653bad5db1..71dd72bdaf 100644 --- a/src/UniGetUI/Controls/MenuForPackage.cs +++ b/src/UniGetUI/Controls/MenuForPackage.cs @@ -38,4 +38,28 @@ public BetterMenuItem() Style = menuStyle; } } + + public partial class BetterToggleMenuItem : ToggleMenuFlyoutItem + { + private readonly Style menuStyle = (Style)Application.Current.Resources["BetterToggleMenuItem"]; + + public IconType IconName + { + set + { + var icon = new LocalIcon(value) { FontSize = 24 }; + Icon = icon; + } + } + + public new string Text + { + set => base.Text = CoreTools.Translate(value); + } + + public BetterToggleMenuItem() + { + Style = menuStyle; + } + } } diff --git a/src/UniGetUI/Controls/ObservablePackageCollection.cs b/src/UniGetUI/Controls/ObservablePackageCollection.cs index 99e0c5c625..41d7170571 100644 --- a/src/UniGetUI/Controls/ObservablePackageCollection.cs +++ b/src/UniGetUI/Controls/ObservablePackageCollection.cs @@ -1,4 +1,5 @@ using UniGetUI.Core.Classes; +using UniGetUI.Interface; using UniGetUI.PackageEngine.Interfaces; namespace UniGetUI.PackageEngine.PackageClasses @@ -17,26 +18,30 @@ public enum Sorter NewVersion, Source, } + public Sorter CurrentSorter { get; private set; } public ObservablePackageCollection() { + CurrentSorter = Sorter.Name; SortingSelector = x => x.Package.Name; } /// /// Add a package to the collection /// - public void Add(IPackage p) + public void Add(IPackage p, AbstractPackagesPage page) { - base.Add(new PackageWrapper(p)); + base.Add(new PackageWrapper(p, page)); } /// /// Sets the property with which to filter the package and sorts the collection /// /// The field with which to sort the collection + /// public void SetSorter(Sorter field) { + CurrentSorter = field; switch (field) { case Sorter.Checked: diff --git a/src/UniGetUI/Controls/PackageWrapper.cs b/src/UniGetUI/Controls/PackageWrapper.cs index 65ae208abd..e105bf314e 100644 --- a/src/UniGetUI/Controls/PackageWrapper.cs +++ b/src/UniGetUI/Controls/PackageWrapper.cs @@ -5,6 +5,7 @@ using Microsoft.UI.Xaml.Media.Imaging; using UniGetUI.Core.Classes; using UniGetUI.Core.Tools; +using UniGetUI.Interface; using UniGetUI.Interface.Enums; using UniGetUI.PackageEngine.Interfaces; @@ -32,6 +33,7 @@ public bool IsChecked public bool AlternateIdIconVisible; public bool ShowCustomPackageIcon; public bool ShowDefaultPackageIcon = true; + public string VersionComboString; public IconType MainIconId = IconType.Id; public IconType AlternateIconId = IconType.Id; public ImageSource? MainIconSource; @@ -57,13 +59,22 @@ public Uri? PackageIcon public IPackage Package { get; private set; } public PackageWrapper Self { get; private set; } - public PackageWrapper(IPackage package) + private readonly AbstractPackagesPage _page; + + public PackageWrapper(IPackage package, AbstractPackagesPage page) { Package = package; Self = this; + _page = page; WhenTagHasChanged(); Package.PropertyChanged += Package_PropertyChanged; UpdatePackageIcon(); + VersionComboString = package.IsUpgradable ? $"{package.VersionString} -> {package.NewVersionString}" : package.VersionString; + } + + public async void RightClick() + { + await _page.ShowContextMenu(this); } public void Package_PropertyChanged(object? sender, PropertyChangedEventArgs e) @@ -167,7 +178,7 @@ public void UpdatePackageIcon() MainIconSource = new BitmapImage { UriSource = icon, - DecodePixelWidth = 24, + DecodePixelWidth = 64, DecodePixelType = DecodePixelType.Logical, }; ShowCustomPackageIcon = true; diff --git a/src/UniGetUI/MainWindow.xaml.cs b/src/UniGetUI/MainWindow.xaml.cs index 143b04714a..b44429727c 100644 --- a/src/UniGetUI/MainWindow.xaml.cs +++ b/src/UniGetUI/MainWindow.xaml.cs @@ -265,7 +265,7 @@ public void HandleNotificationActivation(AppNotificationActivatedEventArgs args) if (action == NotificationArguments.UpdateAllPackages) { - NavigationPage.UpdatesPage.UpdateAll(); + MainApp.Operations.UpdateAll(); } else if (action == NotificationArguments.ShowOnUpdatesTab) { @@ -360,11 +360,11 @@ private void HandleDeepLink(string link) if (Id != "" && CombinedManagerName != "" && ManagerName == "" && SourceName == "") { Logger.Warn($"URI {link} follows old scheme"); - NavigationPage.DiscoverPage.ShowSharedPackage_ThreadSafe(Id, CombinedManagerName); + DialogHelper.ShowSharedPackage_ThreadSafe(Id, CombinedManagerName); } else if (Id != "" && ManagerName != "" && SourceName != "") { - NavigationPage.DiscoverPage.ShowSharedPackage_ThreadSafe(Id, ManagerName, SourceName); + DialogHelper.ShowSharedPackage_ThreadSafe(Id, ManagerName, SourceName); } else { @@ -437,8 +437,7 @@ public void ProcessCommandLineParameters() { // Handle potential JSON files Logger.ImportantInfo("Begin attempt to open the package bundle " + param); - NavigationPage.NavigateTo(PageType.Bundles); - _ = NavigationPage.BundlesPage.OpenFromFile(param); + NavigationPage.LoadBundleFile(param); } else if (param.EndsWith("UniGetUI.exe") || param.EndsWith("UniGetUI.dll")) { diff --git a/src/UniGetUI/Pages/DialogPages/DialogHelper_Packages.cs b/src/UniGetUI/Pages/DialogPages/DialogHelper_Packages.cs index 280cf91cbb..5dc4b8034e 100644 --- a/src/UniGetUI/Pages/DialogPages/DialogHelper_Packages.cs +++ b/src/UniGetUI/Pages/DialogPages/DialogHelper_Packages.cs @@ -1,13 +1,17 @@ +using System.Diagnostics.Tracing; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media; +using UniGetUI.Core.Logging; using UniGetUI.Core.Tools; using UniGetUI.Interface.Dialogs; using UniGetUI.Interface.Telemetry; +using UniGetUI.PackageEngine; using UniGetUI.PackageEngine.Enums; using UniGetUI.PackageEngine.Interfaces; using UniGetUI.PackageEngine.PackageClasses; using UniGetUI.PackageEngine.Serializable; +using Windows.ApplicationModel; namespace UniGetUI.Pages.DialogPages; @@ -139,4 +143,56 @@ public static async Task ConfirmUninstallation(IReadOnlyList pac return await Window.ShowDialogAsync(dialog) is ContentDialogResult.Primary; } + + public static void ShowSharedPackage_ThreadSafe(string id, string combinedSourceName) + { + var contents = combinedSourceName.Split(':'); + string managerName = contents[0]; + string sourceName = ""; + if (contents.Length > 1) sourceName = contents[1]; + GetPackageFromIdAndManager(id, managerName, sourceName, "LEGACY_COMBINEDSOURCE"); + } + + public static void ShowSharedPackage_ThreadSafe(string id, string managerName, string sourceName) + { + MainApp.Instance.MainWindow.DispatcherQueue.TryEnqueue(() => + { + GetPackageFromIdAndManager(id, managerName, sourceName, "DEFAULT"); + }); + } + + private static async void GetPackageFromIdAndManager(string id, string managerName, string sourceName, string eventSource) + { + try + { + Window.Activate(); + ShowLoadingDialog(CoreTools.Translate("Please wait...")); + + var findResult = await Task.Run(() => PEInterface.DiscoveredPackagesLoader.GetPackageFromIdAndManager(id, managerName, sourceName)); + + HideLoadingDialog(); + + if (findResult.Item1 is null) throw new KeyNotFoundException(findResult.Item2 ?? "Unknown error"); + + TelemetryHandler.SharedPackage(findResult.Item1, eventSource); + ShowPackageDetails(findResult.Item1, OperationType.Install, TEL_InstallReferral.FROM_WEB_SHARE); + + } + catch (Exception ex) + { + Logger.Error($"An error occurred while attempting to show the package with id {id}"); + var warningDialog = new ContentDialog + { + Title = CoreTools.Translate("Package not found"), + Content = CoreTools.Translate("An error occurred when attempting to show the package with Id {0}", id) + ":\n" + ex.Message, + CloseButtonText = CoreTools.Translate("Ok"), + DefaultButton = ContentDialogButton.Close, + XamlRoot = MainApp.Instance.MainWindow.Content.XamlRoot // Ensure the dialog is shown in the correct context + }; + + HideLoadingDialog(); + await Window.ShowDialogAsync(warningDialog); + + } + } } diff --git a/src/UniGetUI/Pages/MainView.xaml.cs b/src/UniGetUI/Pages/MainView.xaml.cs index 56e592f5c1..72efe2776a 100644 --- a/src/UniGetUI/Pages/MainView.xaml.cs +++ b/src/UniGetUI/Pages/MainView.xaml.cs @@ -42,10 +42,10 @@ public enum PageType public sealed partial class MainView : UserControl { - public DiscoverSoftwarePage DiscoverPage; - public SoftwareUpdatesPage UpdatesPage; - public InstalledPackagesPage InstalledPage; - public PackageBundlesPage BundlesPage; + private DiscoverSoftwarePage DiscoverPage; + private SoftwareUpdatesPage UpdatesPage; + private InstalledPackagesPage InstalledPage; + private PackageBundlesPage BundlesPage; private SettingsBasePage? SettingsPage; private SettingsBasePage? ManagersPage; private UniGetUILogPage? UniGetUILogPage; @@ -431,5 +431,11 @@ private void MoreNavBtn_Tapped(object sender, Microsoft.UI.Xaml.Input.TappedRout (VersionMenuItem as MenuFlyoutItem).Text = CoreTools.Translate("WingetUI Version {0}", CoreData.VersionName); MoreNavButtonMenu.ShowAt(sender as FrameworkElement); } + + internal void LoadBundleFile(string param) + { + NavigateTo(PageType.Bundles); + BundlesPage?.OpenFromFile(param); + } } } diff --git a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Backup.xaml.cs b/src/UniGetUI/Pages/SettingsPages/GeneralPages/Backup.xaml.cs index dbf726d1d3..81e0c5f4b4 100644 --- a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Backup.xaml.cs +++ b/src/UniGetUI/Pages/SettingsPages/GeneralPages/Backup.xaml.cs @@ -5,6 +5,7 @@ using UniGetUI.Core.Data; using System.Diagnostics; using UniGetUI.Pages.DialogPages; +using UniGetUI.Interface.SoftwarePages; // To learn more about WinUI, the WinUI project structure, // and more about our project templates, see: http://aka.ms/winui-project-info. @@ -99,7 +100,7 @@ private void OpenBackupPath_Click(object sender, RoutedEventArgs e) private async void DoBackup_Click(object sender, EventArgs e) { DialogHelper.ShowLoadingDialog(CoreTools.Translate("Performing backup, please wait...")); - await MainApp.Instance.MainWindow.NavigationPage.InstalledPage.BackupPackages(); + await InstalledPackagesPage.BackupPackages(); DialogHelper.HideLoadingDialog(); } } diff --git a/src/UniGetUI/Pages/SoftwarePages/AbstractPackagesPage.xaml b/src/UniGetUI/Pages/SoftwarePages/AbstractPackagesPage.xaml index 76f81ffb2e..563e91c7e2 100644 --- a/src/UniGetUI/Pages/SoftwarePages/AbstractPackagesPage.xaml +++ b/src/UniGetUI/Pages/SoftwarePages/AbstractPackagesPage.xaml @@ -10,9 +10,11 @@ xmlns:local="using:UniGetUI.Interface" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:pkgClasses="using:UniGetUI.PackageEngine.PackageClasses" + xmlns:ui="using:CommunityToolkit.WinUI" xmlns:widgets="using:UniGetUI.Interface.Widgets" Name="ABSTRACT_PAGE" NavigationCacheMode="Required" + SizeChanged="ABSTRACT_PAGE_SizeChanged" mc:Ignorable="d"> @@ -29,624 +31,7 @@ - - - - - + - - - - - - - - - + + + + - + - - - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -827,98 +479,143 @@ - + - - - + - - - + + + + + - - - - - - + Margin="0,0,4,0" + HorizontalAlignment="Stretch" + Orientation="Vertical" + Spacing="8"> - + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +