diff --git a/src/Models/AvatarManager.cs b/src/Models/AvatarManager.cs index ed39bcf7..a380c1bc 100644 --- a/src/Models/AvatarManager.cs +++ b/src/Models/AvatarManager.cs @@ -1,21 +1,26 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Net.Http; +using System.Security.Cryptography; +using System.Text; +using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using Avalonia.Media.Imaging; +using Avalonia.Platform; using Avalonia.Threading; namespace SourceGit.Models { public interface IAvatarHost { - void OnAvatarResourceChanged(string md5); + void OnAvatarResourceChanged(string email); } - public static class AvatarManager + public static partial class AvatarManager { public static string SelectedServer { @@ -33,29 +38,35 @@ namespace SourceGit.Models { while (true) { - var md5 = null as string; + var email = null as string; lock (_synclock) { foreach (var one in _requesting) { - md5 = one; + email = one; break; } } - if (md5 == null) + if (email == null) { Thread.Sleep(100); continue; } + var md5 = GetEmailHash(email); + var matchGithubUser = REG_GITHUB_USER_EMAIL().Match(email); + var url = matchGithubUser.Success ? + $"https://avatars.githubusercontent.com/{matchGithubUser.Groups[2].Value}" : + $"{SelectedServer}{md5}?d=404"; + var localFile = Path.Combine(_storePath, md5); var img = null as Bitmap; try { var client = new HttpClient() { Timeout = TimeSpan.FromSeconds(2) }; - var task = client.GetAsync($"{SelectedServer}{md5}?d=404"); + var task = client.GetAsync(url); task.Wait(); var rsp = task.Result; @@ -82,13 +93,13 @@ namespace SourceGit.Models lock (_synclock) { - _requesting.Remove(md5); + _requesting.Remove(email); } Dispatcher.UIThread.InvokeAsync(() => { - _resources[md5] = img; - NotifyResourceChanged(md5); + _resources[email] = img; + NotifyResourceChanged(email); }); } }); @@ -104,25 +115,36 @@ namespace SourceGit.Models _avatars.Remove(host); } - public static Bitmap Request(string md5, bool forceRefetch = false) + public static Bitmap Request(string email, bool forceRefetch) { + if (email.Equals("noreply@github.com", StringComparison.Ordinal)) + { + if (_githubEmailAvatar == null) + { + var icon = AssetLoader.Open(new Uri($"avares://SourceGit/Resources/Images/github.png", UriKind.RelativeOrAbsolute)); + _githubEmailAvatar = new Bitmap(icon); + } + + return _githubEmailAvatar; + } + if (forceRefetch) { - if (_resources.ContainsKey(md5)) - _resources.Remove(md5); + if (_resources.ContainsKey(email)) + _resources.Remove(email); - var localFile = Path.Combine(_storePath, md5); + var localFile = Path.Combine(_storePath, GetEmailHash(email)); if (File.Exists(localFile)) File.Delete(localFile); - NotifyResourceChanged(md5); + NotifyResourceChanged(email); } else { - if (_resources.TryGetValue(md5, out var value)) + if (_resources.TryGetValue(email, out var value)) return value; - var localFile = Path.Combine(_storePath, md5); + var localFile = Path.Combine(_storePath, GetEmailHash(email)); if (File.Exists(localFile)) { try @@ -130,7 +152,7 @@ namespace SourceGit.Models using (var stream = File.OpenRead(localFile)) { var img = Bitmap.DecodeToWidth(stream, 128); - _resources.Add(md5, img); + _resources.Add(email, img); return img; } } @@ -143,18 +165,28 @@ namespace SourceGit.Models lock (_synclock) { - if (!_requesting.Contains(md5)) - _requesting.Add(md5); + if (!_requesting.Contains(email)) + _requesting.Add(email); } return null; } - private static void NotifyResourceChanged(string md5) + private static string GetEmailHash(string email) + { + var lowered = email.ToLower(CultureInfo.CurrentCulture).Trim(); + var hash = MD5.Create().ComputeHash(Encoding.Default.GetBytes(lowered)); + var builder = new StringBuilder(); + foreach (var c in hash) + builder.Append(c.ToString("x2")); + return builder.ToString(); + } + + private static void NotifyResourceChanged(string email) { foreach (var avatar in _avatars) { - avatar.OnAvatarResourceChanged(md5); + avatar.OnAvatarResourceChanged(email); } } @@ -163,5 +195,9 @@ namespace SourceGit.Models private static readonly List _avatars = new List(); private static readonly Dictionary _resources = new Dictionary(); private static readonly HashSet _requesting = new HashSet(); + + [GeneratedRegex(@"^(?:(\d+)\+)?(.+?)@users\.noreply\.github\.com$")] + private static partial Regex REG_GITHUB_USER_EMAIL(); + private static Bitmap _githubEmailAvatar = null; } } diff --git a/src/Models/ExternalMerger.cs b/src/Models/ExternalMerger.cs index 9a27db0b..9855f9d3 100644 --- a/src/Models/ExternalMerger.cs +++ b/src/Models/ExternalMerger.cs @@ -20,7 +20,7 @@ namespace SourceGit.Models { get { - var icon = AssetLoader.Open(new Uri($"avares://SourceGit/Resources/ExternalToolIcons/{Icon}.png", UriKind.RelativeOrAbsolute)); + var icon = AssetLoader.Open(new Uri($"avares://SourceGit/Resources/Images/ExternalToolIcons/{Icon}.png", UriKind.RelativeOrAbsolute)); return new Bitmap(icon); } } diff --git a/src/Models/ExternalTool.cs b/src/Models/ExternalTool.cs index 401fb987..5ea8c744 100644 --- a/src/Models/ExternalTool.cs +++ b/src/Models/ExternalTool.cs @@ -25,7 +25,7 @@ namespace SourceGit.Models try { - var asset = AssetLoader.Open(new Uri($"avares://SourceGit/Resources/ExternalToolIcons/{icon}.png", + var asset = AssetLoader.Open(new Uri($"avares://SourceGit/Resources/Images/ExternalToolIcons/{icon}.png", UriKind.RelativeOrAbsolute)); IconImage = new Bitmap(asset); } diff --git a/src/Resources/ExternalToolIcons/JetBrains/CL.png b/src/Resources/Images/ExternalToolIcons/JetBrains/CL.png similarity index 100% rename from src/Resources/ExternalToolIcons/JetBrains/CL.png rename to src/Resources/Images/ExternalToolIcons/JetBrains/CL.png diff --git a/src/Resources/ExternalToolIcons/JetBrains/DB.png b/src/Resources/Images/ExternalToolIcons/JetBrains/DB.png similarity index 100% rename from src/Resources/ExternalToolIcons/JetBrains/DB.png rename to src/Resources/Images/ExternalToolIcons/JetBrains/DB.png diff --git a/src/Resources/ExternalToolIcons/JetBrains/DL.png b/src/Resources/Images/ExternalToolIcons/JetBrains/DL.png similarity index 100% rename from src/Resources/ExternalToolIcons/JetBrains/DL.png rename to src/Resources/Images/ExternalToolIcons/JetBrains/DL.png diff --git a/src/Resources/ExternalToolIcons/JetBrains/DS.png b/src/Resources/Images/ExternalToolIcons/JetBrains/DS.png similarity index 100% rename from src/Resources/ExternalToolIcons/JetBrains/DS.png rename to src/Resources/Images/ExternalToolIcons/JetBrains/DS.png diff --git a/src/Resources/ExternalToolIcons/JetBrains/GO.png b/src/Resources/Images/ExternalToolIcons/JetBrains/GO.png similarity index 100% rename from src/Resources/ExternalToolIcons/JetBrains/GO.png rename to src/Resources/Images/ExternalToolIcons/JetBrains/GO.png diff --git a/src/Resources/ExternalToolIcons/JetBrains/JB.png b/src/Resources/Images/ExternalToolIcons/JetBrains/JB.png similarity index 100% rename from src/Resources/ExternalToolIcons/JetBrains/JB.png rename to src/Resources/Images/ExternalToolIcons/JetBrains/JB.png diff --git a/src/Resources/ExternalToolIcons/JetBrains/PC.png b/src/Resources/Images/ExternalToolIcons/JetBrains/PC.png similarity index 100% rename from src/Resources/ExternalToolIcons/JetBrains/PC.png rename to src/Resources/Images/ExternalToolIcons/JetBrains/PC.png diff --git a/src/Resources/ExternalToolIcons/JetBrains/PS.png b/src/Resources/Images/ExternalToolIcons/JetBrains/PS.png similarity index 100% rename from src/Resources/ExternalToolIcons/JetBrains/PS.png rename to src/Resources/Images/ExternalToolIcons/JetBrains/PS.png diff --git a/src/Resources/ExternalToolIcons/JetBrains/PY.png b/src/Resources/Images/ExternalToolIcons/JetBrains/PY.png similarity index 100% rename from src/Resources/ExternalToolIcons/JetBrains/PY.png rename to src/Resources/Images/ExternalToolIcons/JetBrains/PY.png diff --git a/src/Resources/ExternalToolIcons/JetBrains/QA.png b/src/Resources/Images/ExternalToolIcons/JetBrains/QA.png similarity index 100% rename from src/Resources/ExternalToolIcons/JetBrains/QA.png rename to src/Resources/Images/ExternalToolIcons/JetBrains/QA.png diff --git a/src/Resources/ExternalToolIcons/JetBrains/QD.png b/src/Resources/Images/ExternalToolIcons/JetBrains/QD.png similarity index 100% rename from src/Resources/ExternalToolIcons/JetBrains/QD.png rename to src/Resources/Images/ExternalToolIcons/JetBrains/QD.png diff --git a/src/Resources/ExternalToolIcons/JetBrains/RD.png b/src/Resources/Images/ExternalToolIcons/JetBrains/RD.png similarity index 100% rename from src/Resources/ExternalToolIcons/JetBrains/RD.png rename to src/Resources/Images/ExternalToolIcons/JetBrains/RD.png diff --git a/src/Resources/ExternalToolIcons/JetBrains/RM.png b/src/Resources/Images/ExternalToolIcons/JetBrains/RM.png similarity index 100% rename from src/Resources/ExternalToolIcons/JetBrains/RM.png rename to src/Resources/Images/ExternalToolIcons/JetBrains/RM.png diff --git a/src/Resources/ExternalToolIcons/JetBrains/RR.png b/src/Resources/Images/ExternalToolIcons/JetBrains/RR.png similarity index 100% rename from src/Resources/ExternalToolIcons/JetBrains/RR.png rename to src/Resources/Images/ExternalToolIcons/JetBrains/RR.png diff --git a/src/Resources/ExternalToolIcons/JetBrains/WRS.png b/src/Resources/Images/ExternalToolIcons/JetBrains/WRS.png similarity index 100% rename from src/Resources/ExternalToolIcons/JetBrains/WRS.png rename to src/Resources/Images/ExternalToolIcons/JetBrains/WRS.png diff --git a/src/Resources/ExternalToolIcons/JetBrains/WS.png b/src/Resources/Images/ExternalToolIcons/JetBrains/WS.png similarity index 100% rename from src/Resources/ExternalToolIcons/JetBrains/WS.png rename to src/Resources/Images/ExternalToolIcons/JetBrains/WS.png diff --git a/src/Resources/ExternalToolIcons/beyond_compare.png b/src/Resources/Images/ExternalToolIcons/beyond_compare.png similarity index 100% rename from src/Resources/ExternalToolIcons/beyond_compare.png rename to src/Resources/Images/ExternalToolIcons/beyond_compare.png diff --git a/src/Resources/ExternalToolIcons/codium.png b/src/Resources/Images/ExternalToolIcons/codium.png similarity index 100% rename from src/Resources/ExternalToolIcons/codium.png rename to src/Resources/Images/ExternalToolIcons/codium.png diff --git a/src/Resources/ExternalToolIcons/fleet.png b/src/Resources/Images/ExternalToolIcons/fleet.png similarity index 100% rename from src/Resources/ExternalToolIcons/fleet.png rename to src/Resources/Images/ExternalToolIcons/fleet.png diff --git a/src/Resources/ExternalToolIcons/git.png b/src/Resources/Images/ExternalToolIcons/git.png similarity index 100% rename from src/Resources/ExternalToolIcons/git.png rename to src/Resources/Images/ExternalToolIcons/git.png diff --git a/src/Resources/ExternalToolIcons/kdiff3.png b/src/Resources/Images/ExternalToolIcons/kdiff3.png similarity index 100% rename from src/Resources/ExternalToolIcons/kdiff3.png rename to src/Resources/Images/ExternalToolIcons/kdiff3.png diff --git a/src/Resources/ExternalToolIcons/meld.png b/src/Resources/Images/ExternalToolIcons/meld.png similarity index 100% rename from src/Resources/ExternalToolIcons/meld.png rename to src/Resources/Images/ExternalToolIcons/meld.png diff --git a/src/Resources/ExternalToolIcons/p4merge.png b/src/Resources/Images/ExternalToolIcons/p4merge.png similarity index 100% rename from src/Resources/ExternalToolIcons/p4merge.png rename to src/Resources/Images/ExternalToolIcons/p4merge.png diff --git a/src/Resources/ExternalToolIcons/rider.png b/src/Resources/Images/ExternalToolIcons/rider.png similarity index 100% rename from src/Resources/ExternalToolIcons/rider.png rename to src/Resources/Images/ExternalToolIcons/rider.png diff --git a/src/Resources/ExternalToolIcons/sublime_text.png b/src/Resources/Images/ExternalToolIcons/sublime_text.png similarity index 100% rename from src/Resources/ExternalToolIcons/sublime_text.png rename to src/Resources/Images/ExternalToolIcons/sublime_text.png diff --git a/src/Resources/ExternalToolIcons/tortoise_merge.png b/src/Resources/Images/ExternalToolIcons/tortoise_merge.png similarity index 100% rename from src/Resources/ExternalToolIcons/tortoise_merge.png rename to src/Resources/Images/ExternalToolIcons/tortoise_merge.png diff --git a/src/Resources/ExternalToolIcons/vs.png b/src/Resources/Images/ExternalToolIcons/vs.png similarity index 100% rename from src/Resources/ExternalToolIcons/vs.png rename to src/Resources/Images/ExternalToolIcons/vs.png diff --git a/src/Resources/ExternalToolIcons/vscode.png b/src/Resources/Images/ExternalToolIcons/vscode.png similarity index 100% rename from src/Resources/ExternalToolIcons/vscode.png rename to src/Resources/Images/ExternalToolIcons/vscode.png diff --git a/src/Resources/ExternalToolIcons/vscode_insiders.png b/src/Resources/Images/ExternalToolIcons/vscode_insiders.png similarity index 100% rename from src/Resources/ExternalToolIcons/vscode_insiders.png rename to src/Resources/Images/ExternalToolIcons/vscode_insiders.png diff --git a/src/Resources/ExternalToolIcons/win_merge.png b/src/Resources/Images/ExternalToolIcons/win_merge.png similarity index 100% rename from src/Resources/ExternalToolIcons/win_merge.png rename to src/Resources/Images/ExternalToolIcons/win_merge.png diff --git a/src/Resources/ExternalToolIcons/xcode.png b/src/Resources/Images/ExternalToolIcons/xcode.png similarity index 100% rename from src/Resources/ExternalToolIcons/xcode.png rename to src/Resources/Images/ExternalToolIcons/xcode.png diff --git a/src/Resources/ShellIcons/cmd.png b/src/Resources/Images/ShellIcons/cmd.png similarity index 100% rename from src/Resources/ShellIcons/cmd.png rename to src/Resources/Images/ShellIcons/cmd.png diff --git a/src/Resources/ShellIcons/git-bash.png b/src/Resources/Images/ShellIcons/git-bash.png similarity index 100% rename from src/Resources/ShellIcons/git-bash.png rename to src/Resources/Images/ShellIcons/git-bash.png diff --git a/src/Resources/ShellIcons/pwsh.png b/src/Resources/Images/ShellIcons/pwsh.png similarity index 100% rename from src/Resources/ShellIcons/pwsh.png rename to src/Resources/Images/ShellIcons/pwsh.png diff --git a/src/Resources/ShellIcons/wt.png b/src/Resources/Images/ShellIcons/wt.png similarity index 100% rename from src/Resources/ShellIcons/wt.png rename to src/Resources/Images/ShellIcons/wt.png diff --git a/src/Resources/Images/github.png b/src/Resources/Images/github.png new file mode 100644 index 00000000..3a7abb16 Binary files /dev/null and b/src/Resources/Images/github.png differ diff --git a/src/SourceGit.csproj b/src/SourceGit.csproj index ecb55bd2..009d838f 100644 --- a/src/SourceGit.csproj +++ b/src/SourceGit.csproj @@ -29,10 +29,11 @@ - - - + + + + diff --git a/src/Views/Avatar.cs b/src/Views/Avatar.cs index 62c853b7..7daa425a 100644 --- a/src/Views/Avatar.cs +++ b/src/Views/Avatar.cs @@ -1,7 +1,5 @@ using System; using System.Globalization; -using System.Security.Cryptography; -using System.Text; using Avalonia; using Avalonia.Controls; @@ -42,7 +40,7 @@ namespace SourceGit.Views { if (User != null) { - Models.AvatarManager.Request(_emailMD5, true); + Models.AvatarManager.Request(User.Email, true); InvalidateVisual(); } }; @@ -59,7 +57,7 @@ namespace SourceGit.Views return; var corner = (float)Math.Max(2, Bounds.Width / 16); - var img = Models.AvatarManager.Request(_emailMD5); + var img = Models.AvatarManager.Request(User.Email, false); if (img != null) { var rect = new Rect(0, 0, Bounds.Width, Bounds.Height); @@ -74,9 +72,9 @@ namespace SourceGit.Views } } - public void OnAvatarResourceChanged(string md5) + public void OnAvatarResourceChanged(string email) { - if (_emailMD5 == md5) + if (User.Email.Equals(email, StringComparison.Ordinal)) { InvalidateVisual(); } @@ -97,10 +95,7 @@ namespace SourceGit.Views private static void OnUserPropertyChanged(Avatar avatar, AvaloniaPropertyChangedEventArgs e) { if (avatar.User == null) - { - avatar._emailMD5 = null; return; - } var placeholder = string.IsNullOrWhiteSpace(avatar.User.Name) ? "?" : avatar.User.Name.Substring(0, 1); var chars = placeholder.ToCharArray(); @@ -108,15 +103,6 @@ namespace SourceGit.Views foreach (var c in chars) sum += Math.Abs(c); - var lowered = avatar.User.Email.ToLower(CultureInfo.CurrentCulture).Trim(); - var hash = MD5.Create().ComputeHash(Encoding.Default.GetBytes(lowered)); - var builder = new StringBuilder(); - foreach (var c in hash) - builder.Append(c.ToString("x2")); - var md5 = builder.ToString(); - if (avatar._emailMD5 == null || avatar._emailMD5 != md5) - avatar._emailMD5 = md5; - avatar._fallbackBrush = new LinearGradientBrush { GradientStops = FALLBACK_GRADIENTS[sum % FALLBACK_GRADIENTS.Length], @@ -139,6 +125,5 @@ namespace SourceGit.Views private FormattedText _fallbackLabel = null; private LinearGradientBrush _fallbackBrush = null; - private string _emailMD5 = null; } } diff --git a/src/Views/Preference.axaml b/src/Views/Preference.axaml index 31df2fab..f254a735 100644 --- a/src/Views/Preference.axaml +++ b/src/Views/Preference.axaml @@ -294,22 +294,22 @@ IsVisible="{OnPlatform False, Windows=True}"> - + - + - + - +