diff --git a/src/Models/AvatarManager.cs b/src/Models/AvatarManager.cs index b5f90ae0..4d81e112 100644 --- a/src/Models/AvatarManager.cs +++ b/src/Models/AvatarManager.cs @@ -9,10 +9,15 @@ using System.Threading.Tasks; namespace SourceGit.Models { public interface IAvatarHost { - void OnAvatarResourceReady(string md5, Bitmap bitmap); + void OnAvatarResourceChanged(string md5); } public static class AvatarManager { + public static string SelectedServer { + get; + set; + } = "https://www.gravatar.com/avatar/"; + static AvatarManager() { _storePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "SourceGit", "avatars"); if (!Directory.Exists(_storePath)) Directory.CreateDirectory(_storePath); @@ -37,7 +42,7 @@ namespace SourceGit.Models { var img = null as Bitmap; try { var client = new HttpClient() { Timeout = TimeSpan.FromSeconds(2) }; - var task = client.GetAsync($"https://cravatar.cn/avatar/{md5}?d=404"); + var task = client.GetAsync($"{SelectedServer}{md5}?d=404"); task.Wait(); var rsp = task.Result; @@ -61,19 +66,28 @@ namespace SourceGit.Models { Dispatcher.UIThread.InvokeAsync(() => { if (_resources.ContainsKey(md5)) _resources[md5] = img; else _resources.Add(md5, img); - if (img != null) NotifyResourceReady(md5, img); + NotifyResourceChanged(md5); }); } }); } public static void Subscribe(IAvatarHost host) { - _avatars.Add(new WeakReference(host)); + _avatars.Add(host); + } + + public static void Unsubscribe(IAvatarHost host) { + _avatars.Remove(host); } public static Bitmap Request(string md5, bool forceRefetch = false) { if (forceRefetch) { - if (_resources.ContainsKey(md5)) _resources.Remove(md5); + if (_resources.ContainsKey(md5)) _resources.Remove(md5); + + var localFile = Path.Combine(_storePath, md5); + if (File.Exists(localFile)) File.Delete(localFile); + + NotifyResourceChanged(md5); } else { if (_resources.ContainsKey(md5)) return _resources[md5]; @@ -96,24 +110,15 @@ namespace SourceGit.Models { return null; } - private static void NotifyResourceReady(string md5, Bitmap bitmap) { - List> invalids = new List>(); + private static void NotifyResourceChanged(string md5) { foreach (var avatar in _avatars) { - IAvatarHost retrived = null; - if (avatar.TryGetTarget(out retrived)) { - retrived.OnAvatarResourceReady(md5, bitmap); - break; - } else { - invalids.Add(avatar); - } + avatar.OnAvatarResourceChanged(md5); } - - foreach (var invalid in invalids) _avatars.Remove(invalid); } private static object _synclock = new object(); private static string _storePath = string.Empty; - private static List> _avatars = new List>(); + private static List _avatars = new List(); private static Dictionary _resources = new Dictionary(); private static HashSet _requesting = new HashSet(); } diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 5459b17c..5c58a754 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -271,16 +271,12 @@ Fast-Forward (without checkout) File History - USE THIS VERSION CHANGE DISPLAY MODE Show as Grid Show as List Show as Tree - SELECT FOLDER - SELECTED : - Histories SEARCH SHA/SUBJECT/AUTHOR. PRESS ENTER TO SEARCH, ESC TO QUIT CLEAR @@ -380,6 +376,7 @@ Preference GENERAL Language + Avatar Server Theme History Commits Restore windows diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 767aa023..bab81474 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -270,16 +270,12 @@ 快进(无需Checkout) 文件历史 - 使用该版本 切换变更显示模式 网格模式 列表模式 树形模式 - 选择目录... - 当前选择 : - 历史记录 查询提交指纹、信息、作者。回车键开始,ESC键取消 清空 @@ -379,6 +375,7 @@ 偏好设置 通用配置 显示语言 + 头像服务 主题 最大历史提交数 启动时恢复上次打开的仓库 diff --git a/src/ViewModels/Preference.cs b/src/ViewModels/Preference.cs index 49a6b543..e0f9c173 100644 --- a/src/ViewModels/Preference.cs +++ b/src/ViewModels/Preference.cs @@ -51,6 +51,16 @@ namespace SourceGit.ViewModels { } } + public string AvatarServer { + get => Models.AvatarManager.SelectedServer; + set { + if (Models.AvatarManager.SelectedServer != value) { + Models.AvatarManager.SelectedServer = value; + OnPropertyChanged(nameof(AvatarServer)); + } + } + } + public int MaxHistoryCommits { get => _maxHistoryCommits; set => SetProperty(ref _maxHistoryCommits, value); diff --git a/src/Views/Avatar.cs b/src/Views/Avatar.cs index 3feb04bb..d9272959 100644 --- a/src/Views/Avatar.cs +++ b/src/Views/Avatar.cs @@ -1,7 +1,7 @@ using Avalonia; using Avalonia.Controls; +using Avalonia.Interactivity; using Avalonia.Media; -using Avalonia.Media.Imaging; using System; using System.Globalization; using System.Security.Cryptography; @@ -42,25 +42,24 @@ namespace SourceGit.Views { var refetch = new MenuItem() { Header = App.Text("RefetchAvatar") }; refetch.Click += (o, e) => { if (User != null) { - _image = Models.AvatarManager.Request(_emailMD5, true); + Models.AvatarManager.Request(_emailMD5, true); InvalidateVisual(); } }; ContextMenu = new ContextMenu(); ContextMenu.Items.Add(refetch); - - Models.AvatarManager.Subscribe(this); } public override void Render(DrawingContext context) { if (User == null) return; - float corner = (float)Math.Max(2, Bounds.Width / 16); - if (_image != null) { + var corner = (float)Math.Max(2, Bounds.Width / 16); + var img = Models.AvatarManager.Request(_emailMD5, false); + if (img != null) { var rect = new Rect(0, 0, Bounds.Width, Bounds.Height); context.PushClip(new RoundedRect(rect, corner)); - context.DrawImage(_image, rect); + context.DrawImage(img, rect); } else { Point textOrigin = new Point((Bounds.Width - _fallbackLabel.Width) * 0.5, (Bounds.Height - _fallbackLabel.Height) * 0.5); context.DrawRectangle(_fallbackBrush, null, new Rect(0, 0, Bounds.Width, Bounds.Height), corner, corner); @@ -68,13 +67,22 @@ namespace SourceGit.Views { } } - public void OnAvatarResourceReady(string md5, Bitmap bitmap) { + public void OnAvatarResourceChanged(string md5) { if (_emailMD5 == md5) { - _image = bitmap; InvalidateVisual(); } } + protected override void OnLoaded(RoutedEventArgs e) { + base.OnLoaded(e); + Models.AvatarManager.Subscribe(this); + } + + protected override void OnUnloaded(RoutedEventArgs e) { + base.OnUnloaded(e); + Models.AvatarManager.Unsubscribe(this); + } + private static void OnUserPropertyChanged(Avatar avatar, AvaloniaPropertyChangedEventArgs e) { if (avatar.User == null) { avatar._emailMD5 = null; @@ -90,10 +98,7 @@ namespace SourceGit.Views { var builder = new StringBuilder(); foreach (var c in hash) builder.Append(c.ToString("x2")); var md5 = builder.ToString(); - if (avatar._emailMD5 != md5) { - avatar._emailMD5 = md5; - avatar._image = Models.AvatarManager.Request(md5, false); - } + if (avatar._emailMD5 != md5) avatar._emailMD5 = md5; avatar._fallbackBrush = new LinearGradientBrush { GradientStops = FALLBACK_GRADIENTS[sum % FALLBACK_GRADIENTS.Length], @@ -117,6 +122,5 @@ namespace SourceGit.Views { private FormattedText _fallbackLabel = null; private LinearGradientBrush _fallbackBrush = null; private string _emailMD5 = null; - private Bitmap _image = null; } } diff --git a/src/Views/Preference.axaml b/src/Views/Preference.axaml index 4417db65..081c748f 100644 --- a/src/Views/Preference.axaml +++ b/src/Views/Preference.axaml @@ -2,6 +2,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:m="using:SourceGit.Models" xmlns:c="using:SourceGit.Converters" xmlns:vm="using:SourceGit.ViewModels" @@ -56,7 +57,7 @@ - + + + https://www.gravatar.com/avatar/ + https://cravatar.cn/avatar/ + + + + + - - + - -