diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 6dde0a5540..11b582e637 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -2,11 +2,14 @@
enable
10.0.19041.0
- 10.0.26100.56
+ 10.0.26100.57
8.0.405
Martà Climent and the contributors
Martà Climent
enable
+
+
+
true
diff --git a/src/UniGetUI.Core.Tools/Tools.cs b/src/UniGetUI.Core.Tools/Tools.cs
index f6e1bd33c3..fb9dc2f10e 100644
--- a/src/UniGetUI.Core.Tools/Tools.cs
+++ b/src/UniGetUI.Core.Tools/Tools.cs
@@ -146,10 +146,11 @@ public static Tuple Which(string command, bool updateEnv = true)
///
/// A string containing the Id of a package
/// The formatted string
- public static string FormatAsName(string name)
+ public static string FormatAsName(string name, bool isWinget = false)
{
- name =
- name.Replace(".install", "").Replace(".portable", "").Replace("-", " ").Replace("_", " ").Split("/")[^1]
+ if (isWinget) name = string.Join(' ', name.Split('.')[1..]);
+
+ name = name.Replace(".install", "").Replace(".portable", "").Replace("-", " ").Replace("_", " ").Split("/")[^1]
.Split(":")[0];
string newName = "";
for (int i = 0; i < name.Length; i++)
@@ -195,20 +196,46 @@ public static void ReportFatalException(Exception e)
}
string Error_String = $@"
- OS: {Environment.OSVersion.Platform}
- Version: {Environment.OSVersion.VersionString}
- OS Architecture: {Environment.Is64BitOperatingSystem}
- APP Architecture: {Environment.Is64BitProcess}
+ Windows version: {Environment.OSVersion.VersionString}
Language: {LangName}
APP Version: {CoreData.VersionName}
+ APP Build number: {CoreData.BuildNumber}
Executable: {Environment.ProcessPath}
-Crash HResult: {(uint)e.HResult}
+Crash HResult: 0x{(uint)e.HResult:X} ({(uint)e.HResult}, {e.HResult})
Crash Message: {e.Message}
Crash Traceback:
{e.StackTrace}";
+ try
+ {
+ int i = 0;
+ while (e.InnerException is not null)
+ {
+ i++;
+ e = e.InnerException;
+ Error_String += $@"
+
+
+---------------------
+Inner exception ({i}):
+Crash HResult: 0x{(uint)e.HResult:X} ({(uint)e.HResult}, {e.HResult})
+Crash Message: {e.Message}
+
+Crash Traceback:
+{e.StackTrace}";
+ }
+
+ if (i == 0)
+ {
+ Error_String += $"\n\n\nNo inner exceptions found";
+ }
+ } catch
+ {
+ // ignore
+ }
+
Console.WriteLine(Error_String);
string ErrorBody = "https://www.marticliment.com/error-report/?appName=UniGetUI^&errorBody=" +
diff --git a/src/UniGetUI.Interface.Telemetry/TelemetryHandler.cs b/src/UniGetUI.Interface.Telemetry/TelemetryHandler.cs
index 991e35cb29..ed35f92679 100644
--- a/src/UniGetUI.Interface.Telemetry/TelemetryHandler.cs
+++ b/src/UniGetUI.Interface.Telemetry/TelemetryHandler.cs
@@ -15,6 +15,7 @@ public enum TEL_InstallReferral
FROM_BUNDLE,
FROM_WEB_SHARE,
ALREADY_INSTALLED,
+ FROM_RANKING
}
public enum TEL_OP_RESULT
diff --git a/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/Classes/PackageCacher.cs b/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/Classes/PackageCacher.cs
index aec03dbd7f..713c842c02 100644
--- a/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/Classes/PackageCacher.cs
+++ b/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/Classes/PackageCacher.cs
@@ -1,9 +1,10 @@
using System.Collections.Concurrent;
+using UniGetUI.PackageEngine.Interfaces;
using UniGetUI.PackageEngine.PackageClasses;
namespace UniGetUI.PackageEngine.Classes.Packages
{
- internal static class PackageCacher
+ public static class PackageCacher
{
private static readonly ConcurrentDictionary __available_pkgs = [];
private static readonly ConcurrentDictionary __upgradable_pkgs = [];
@@ -116,5 +117,15 @@ private static void AddPackageToCache(Package package, ConcurrentDictionary Source.Name switch
- {
- "Steam" => id.ToLower().Split("\\")[^1].Replace("steam app ", "steam:").Trim(),
- "Local PC" => id.ToLower().Split("\\")[^1],
- "Microsoft Store" => id.IndexOf('_') < id.IndexOf('.') ? // If the first underscore is before the period, this ID has no publisher
- string.Join('_', id.ToLower().Split("\\")[1].Split("_")[0..^4]) : // no publisher: remove `MSIX\`, then the standard ending _version_arch__{random id}
- string.Join('_', string.Join('.', id.ToLower().Split(".")[1..]).Split("_")[0..^4]), // remove the publisher (before the first .), then the standard _version_arch__{random id}
- _ => string.Join('.', id.ToLower().Split(".")[1..]),
- },
- "Scoop" => id.ToLower().Replace(".app", ""),
- "Chocolatey" => id.ToLower().Replace(".install", "").Replace(".portable", ""),
- "vcpkg" => id.ToLower().Split(":")[0].Split("[")[0],
- _ => id.ToLower()
- };
+ _iconId = NormalizeIconId(id, Manager.Name, Source.Name);
}
///
@@ -134,6 +119,26 @@ public Package(
NormalizedNewVersion = CoreTools.VersionStringToStruct(new_version);
}
+ public static string NormalizeIconId(string id, string managerName, string sourceName = "")
+ {
+ return managerName switch
+ {
+ "Winget" => sourceName switch
+ {
+ "Steam" => id.ToLower().Split("\\")[^1].Replace("steam app ", "steam:").Trim(),
+ "Local PC" => id.ToLower().Split("\\")[^1],
+ "Microsoft Store" => id.IndexOf('_') < id.IndexOf('.') ? // If the first underscore is before the period, this ID has no publisher
+ string.Join('_', id.ToLower().Split("\\")[1].Split("_")[0..^4]) : // no publisher: remove `MSIX\`, then the standard ending _version_arch__{random id}
+ string.Join('_', string.Join('.', id.ToLower().Split(".")[1..]).Split("_")[0..^4]), // remove the publisher (before the first .), then the standard _version_arch__{random id}
+ _ => string.Join('.', id.ToLower().Split(".")[1..]),
+ },
+ "Scoop" => id.ToLower().Replace(".app", ""),
+ "Chocolatey" => id.ToLower().Replace(".install", "").Replace(".portable", ""),
+ "vcpkg" => id.ToLower().Split(":")[0].Split("[")[0],
+ _ => id.ToLower()
+ };
+ }
+
public long GetHash()
=> __hash;
diff --git a/src/UniGetUI/Controls/PackageRanking.xaml b/src/UniGetUI/Controls/PackageRanking.xaml
new file mode 100644
index 0000000000..470273b53c
--- /dev/null
+++ b/src/UniGetUI/Controls/PackageRanking.xaml
@@ -0,0 +1,84 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/UniGetUI/Controls/PackageRanking.xaml.cs b/src/UniGetUI/Controls/PackageRanking.xaml.cs
new file mode 100644
index 0000000000..d42d74d467
--- /dev/null
+++ b/src/UniGetUI/Controls/PackageRanking.xaml.cs
@@ -0,0 +1,199 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices.WindowsRuntime;
+using Windows.Foundation;
+using Windows.Foundation.Collections;
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+using Microsoft.UI.Xaml.Controls.Primitives;
+using Microsoft.UI.Xaml.Data;
+using Microsoft.UI.Xaml.Input;
+using Microsoft.UI.Xaml.Media;
+using Microsoft.UI.Xaml.Navigation;
+using System.Collections.ObjectModel;
+using System.Text.Json;
+using UniGetUI.Core.Logging;
+using UniGetUI.Core.Tools;
+using UniGetUI.PackageEngine.Interfaces;
+using Windows.Security.Cryptography.Core;
+using UniGetUI.Core.IconEngine;
+using UniGetUI.PackageEngine.PackageClasses;
+using System.ComponentModel;
+using Windows.Devices.SmartCards;
+using System.Diagnostics;
+using UniGetUI.PackageEngine.Classes.Packages;
+using UniGetUI.PackageEngine;
+using Windows.ApplicationModel.Activation;
+using UniGetUI.Pages.DialogPages;
+using UniGetUI.Interface.Telemetry;
+using UniGetUI.PackageEngine.Enums;
+using Microsoft.UI.Xaml.Documents;
+
+// To learn more about WinUI, the WinUI project structure,
+// and more about our project templates, see: http://aka.ms/winui-project-info.
+
+namespace UniGetUI.Controls
+{
+ public sealed partial class PackagesRanking : UserControl
+ {
+ public ObservableCollection PopularRank { get; } = new();
+ public ObservableCollection InstalledRank { get; } = new();
+ public ObservableCollection WallOfShame { get; } = new();
+
+ public PackagesRanking()
+ {
+ this.InitializeComponent();
+
+ (PopularDescription.Blocks[0] as Paragraph)?.Inlines.Add(new Run { Text = CoreTools.Translate("The most popular packages among UniGetUI users.") + " " });
+ var link = new Hyperlink { NavigateUri = new Uri("about:blank") };
+ link.Inlines.Add(new Run() { Text = CoreTools.Translate("More info") });
+ (PopularDescription.Blocks[0] as Paragraph)?.Inlines.Add(link);
+
+ (InstalledDescription.Blocks[0] as Paragraph)?.Inlines.Add(new Run { Text = CoreTools.Translate("Packages installed the most through UniGetUI.") + " " });
+ link = new Hyperlink { NavigateUri = new Uri("about:blank") };
+ link.Inlines.Add(new Run() { Text = CoreTools.Translate("More info") });
+ (InstalledDescription.Blocks[0] as Paragraph)?.Inlines.Add(link);
+
+ (UninstalledDescription.Blocks[0] as Paragraph)?.Inlines.Add(new Run { Text = CoreTools.Translate("The packages that have been uninstalled most times through UniGetUI.") + " " });
+ link = new Hyperlink { NavigateUri = new Uri("about:blank") };
+ link.Inlines.Add(new Run() { Text = CoreTools.Translate("More info") });
+ (UninstalledDescription.Blocks[0] as Paragraph)?.Inlines.Add(link);
+ }
+
+ public void ReloadButton_Click(object sender, RoutedEventArgs e)
+ {
+ _ = Reload();
+ }
+
+ public async Task Reload()
+ {
+ try
+ {
+ PopularRank.Clear();
+ InstalledRank.Clear();
+ WallOfShame.Clear();
+ ReloadRing.Visibility = Visibility.Visible;
+
+ using (HttpClient client = new HttpClient())
+ {
+ var response = await client.GetStringAsync("https://marticliment.com/unigetui/rankings/daily-ranking.json");
+ var rankings = JsonSerializer.Deserialize(response);
+
+ foreach (var item in rankings.popular[0..5])
+ {
+ PopularRank.Add(new(item));
+ }
+
+ foreach (var item in rankings.installed[0..5])
+ {
+ InstalledRank.Add(new(item));
+ }
+
+ foreach (var item in rankings.uninstalled[0..5])
+ {
+ WallOfShame.Add(new(item));
+ }
+ }
+
+ ReloadRing.Visibility = Visibility.Collapsed;
+ }
+ catch (Exception ex)
+ {
+ Logger.Error(ex);
+ Logger.Error("Could not load package rankings:");
+ ReloadRing.Visibility = Visibility.Collapsed;
+ }
+ }
+ }
+
+ public class Rankings
+ {
+ public long timestamp_utc_seconds { get; set; }
+ public List> popular { get; set; } = new();
+ public List> installed { get; set; } = new();
+ public List> uninstalled { get; set; } = new();
+ }
+
+ public class PackageRankingItem: INotifyPropertyChanged
+ {
+ public string Name { get; set; }
+ public string ManagerName { get; set; }
+ public Uri IconUri = new Uri("ms-appx:///Assets/Images/package_color.png");
+
+ private string id;
+ private string manager;
+ private string source;
+ private IPackage package;
+
+ public event PropertyChangedEventHandler? PropertyChanged;
+
+ public PackageRankingItem(List