feature<Avatar>: add context menu to manually re-fetch avatar

This commit is contained in:
leo 2023-10-11 18:23:33 +08:00
parent e4c350f189
commit 738daddbc7

View file

@ -71,7 +71,9 @@ namespace SourceGit.Views.Controls {
set { SetValue(FallbackLabelProperty, value); } set { SetValue(FallbackLabelProperty, value); }
} }
private static Dictionary<string, List<Avatar>> requesting = new Dictionary<string, List<Avatar>>(); private static event Action<string> RefetchRequested;
private static event Action<string> FetchCompleted;
private static Dictionary<string, BitmapImage> loaded = new Dictionary<string, BitmapImage>(); private static Dictionary<string, BitmapImage> loaded = new Dictionary<string, BitmapImage>();
private static Task loader = null; private static Task loader = null;
@ -79,23 +81,45 @@ namespace SourceGit.Views.Controls {
private FormattedText label = null; private FormattedText label = null;
public Avatar() { public Avatar() {
RefetchRequested += email => {
if (email == Email) {
Source = null;
InvalidateVisual();
}
};
FetchCompleted += email => {
if (email == Email) {
Source = loaded[Email];
InvalidateVisual();
}
};
SetValue(RenderOptions.BitmapScalingModeProperty, BitmapScalingMode.HighQuality); SetValue(RenderOptions.BitmapScalingModeProperty, BitmapScalingMode.HighQuality);
SetValue(RenderOptions.ClearTypeHintProperty, ClearTypeHint.Auto); SetValue(RenderOptions.ClearTypeHintProperty, ClearTypeHint.Auto);
Unloaded += (o, e) => Cancel(Email);
var refetch = new MenuItem();
refetch.Header = App.Text("Dashboard.Refresh");
refetch.Click += (o, e) => Refetch();
ContextMenu = new ContextMenu();
ContextMenu.Items.Add(refetch);
} }
/// <summary> /// <summary>
/// 取消一个下载任务 /// 手动刷新
/// </summary> /// </summary>
/// <param name="email"></param> private void Refetch() {
private void Cancel(string email) { byte[] hash = MD5.Create().ComputeHash(Encoding.Default.GetBytes(Email.ToLower().Trim()));
if (!string.IsNullOrEmpty(email) && requesting.ContainsKey(email)) { string md5 = "";
if (requesting[email].Count <= 1) { for (int i = 0; i < hash.Length; i++) md5 += hash[i].ToString("x2");
requesting.Remove(email); md5 = md5.ToLower();
} else { string filePath = Path.Combine(CACHE_PATH, md5);
requesting[email].Remove(this); if (File.Exists(filePath)) File.Delete(filePath);
}
} RefetchRequested?.Invoke(Email);
if (loaded.ContainsKey(Email)) loaded.Remove(Email);
OnEmailChanged(this, new DependencyPropertyChangedEventArgs(EmailProperty, null, Email));
} }
/// <summary> /// <summary>
@ -146,6 +170,7 @@ namespace SourceGit.Views.Controls {
var chars = placeholder.ToCharArray(); var chars = placeholder.ToCharArray();
foreach (var ch in chars) a.colorIdx += Math.Abs(ch); foreach (var ch in chars) a.colorIdx += Math.Abs(ch);
a.colorIdx = a.colorIdx % BACKGROUND_BRUSHES.Length; a.colorIdx = a.colorIdx % BACKGROUND_BRUSHES.Length;
if (a.Source == null) a.InvalidateVisual();
} }
/// <summary> /// <summary>
@ -157,9 +182,10 @@ namespace SourceGit.Views.Controls {
Avatar a = d as Avatar; Avatar a = d as Avatar;
if (a == null) return; if (a == null) return;
a.Cancel(e.OldValue as string); if (a.Source != null) {
a.Source = null; a.Source = null;
a.InvalidateVisual(); a.InvalidateVisual();
}
var email = e.NewValue as string; var email = e.NewValue as string;
if (string.IsNullOrEmpty(email)) return; if (string.IsNullOrEmpty(email)) return;
@ -169,11 +195,6 @@ namespace SourceGit.Views.Controls {
return; return;
} }
if (requesting.ContainsKey(email)) {
requesting[email].Add(a);
return;
}
byte[] hash = MD5.Create().ComputeHash(Encoding.Default.GetBytes(email.ToLower().Trim())); byte[] hash = MD5.Create().ComputeHash(Encoding.Default.GetBytes(email.ToLower().Trim()));
string md5 = ""; string md5 = "";
for (int i = 0; i < hash.Length; i++) md5 += hash[i].ToString("x2"); for (int i = 0; i < hash.Length; i++) md5 += hash[i].ToString("x2");
@ -181,18 +202,15 @@ namespace SourceGit.Views.Controls {
string filePath = Path.Combine(CACHE_PATH, md5); string filePath = Path.Combine(CACHE_PATH, md5);
if (File.Exists(filePath)) { if (File.Exists(filePath)) {
var img = new BitmapImage(new Uri(filePath)); var img = LoadFromFile(filePath);
loaded.Add(email, img); loaded.Add(email, img);
a.Source = img; a.Source = img;
return; return;
} }
requesting.Add(email, new List<Avatar>()); loaded.Add(email, null);
requesting[email].Add(a);
Action job = () => { Action job = () => {
if (!requesting.ContainsKey(email)) return;
try { try {
var req = WebRequest.CreateHttp($"https://cravatar.cn/avatar/{md5}?d=404"); var req = WebRequest.CreateHttp($"https://cravatar.cn/avatar/{md5}?d=404");
req.Timeout = 2000; req.Timeout = 2000;
@ -200,27 +218,18 @@ namespace SourceGit.Views.Controls {
var rsp = req.GetResponse() as HttpWebResponse; var rsp = req.GetResponse() as HttpWebResponse;
if (rsp != null && rsp.StatusCode == HttpStatusCode.OK) { if (rsp != null && rsp.StatusCode == HttpStatusCode.OK) {
using (var reader = rsp.GetResponseStream()) using (var reader = rsp.GetResponseStream()) {
using (var writer = File.OpenWrite(filePath)) { using (var writer = File.OpenWrite(filePath)) {
reader.CopyTo(writer); reader.CopyTo(writer);
}
} }
a.Dispatcher.Invoke(() => { a.Dispatcher.Invoke(() => {
var img = new BitmapImage(new Uri(filePath)); loaded[email] = LoadFromFile(filePath);
loaded.Add(email, img); FetchCompleted?.Invoke(email);
if (requesting.ContainsKey(email)) {
foreach (var one in requesting[email]) one.Source = img;
}
}); });
} else {
if (!loaded.ContainsKey(email)) loaded.Add(email, null);
} }
} catch { } catch {}
if (!loaded.ContainsKey(email)) loaded.Add(email, null);
}
requesting.Remove(email);
}; };
if (loader != null && !loader.IsCompleted) { if (loader != null && !loader.IsCompleted) {
@ -229,5 +238,13 @@ namespace SourceGit.Views.Controls {
loader = Task.Run(job); loader = Task.Run(job);
} }
} }
private static BitmapImage LoadFromFile(string file) {
var img = new BitmapImage();
img.BeginInit();
img.StreamSource = new MemoryStream(File.ReadAllBytes(file));
img.EndInit();
return img;
}
} }
} }