2021-03-31 04:47:37 -07:00
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Globalization;
|
|
|
|
using System.IO;
|
2021-03-31 19:54:05 -07:00
|
|
|
using System.Net;
|
2021-03-31 04:47:37 -07:00
|
|
|
using System.Security.Cryptography;
|
|
|
|
using System.Text;
|
2021-03-31 19:54:05 -07:00
|
|
|
using System.Threading.Tasks;
|
2021-03-31 04:47:37 -07:00
|
|
|
using System.Windows;
|
|
|
|
using System.Windows.Controls;
|
|
|
|
using System.Windows.Media;
|
|
|
|
using System.Windows.Media.Imaging;
|
|
|
|
|
|
|
|
namespace SourceGit.Helpers {
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Avatar control
|
|
|
|
/// </summary>
|
|
|
|
public class Avatar : Image {
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Colors used in avatar
|
|
|
|
/// </summary>
|
|
|
|
public static Brush[] Colors = new Brush[] {
|
|
|
|
Brushes.DarkCyan,
|
|
|
|
Brushes.DarkGoldenrod,
|
|
|
|
Brushes.DarkGreen,
|
|
|
|
Brushes.DarkKhaki,
|
|
|
|
Brushes.DarkMagenta,
|
|
|
|
Brushes.DarkOliveGreen,
|
|
|
|
Brushes.DarkOrange,
|
|
|
|
Brushes.DarkOrchid,
|
|
|
|
Brushes.DarkSalmon,
|
|
|
|
Brushes.DarkSeaGreen,
|
|
|
|
Brushes.DarkSlateBlue,
|
|
|
|
Brushes.DarkSlateGray,
|
|
|
|
Brushes.DarkTurquoise,
|
|
|
|
Brushes.DarkViolet
|
|
|
|
};
|
|
|
|
|
2021-03-31 19:54:05 -07:00
|
|
|
/// <summary>
|
|
|
|
/// Path to cache downloaded avatars
|
|
|
|
/// </summary>
|
|
|
|
public static readonly string CACHE_PATH = Path.Combine(
|
|
|
|
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
|
|
|
"SourceGit",
|
|
|
|
"avatars");
|
|
|
|
|
2021-03-31 04:47:37 -07:00
|
|
|
/// <summary>
|
|
|
|
/// User property definition.
|
|
|
|
/// </summary>
|
|
|
|
public static readonly DependencyProperty UserProperty = DependencyProperty.Register(
|
|
|
|
"User",
|
|
|
|
typeof(Git.User),
|
|
|
|
typeof(Avatar),
|
|
|
|
new PropertyMetadata(null, OnUserChanged));
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// User property
|
|
|
|
/// </summary>
|
|
|
|
public Git.User User {
|
|
|
|
get { return (Git.User)GetValue(UserProperty); }
|
|
|
|
set { SetValue(UserProperty, value); }
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
2021-03-31 19:54:05 -07:00
|
|
|
/// Current requests.
|
2021-03-31 04:47:37 -07:00
|
|
|
/// </summary>
|
2021-03-31 19:54:05 -07:00
|
|
|
private static Dictionary<string, List<Avatar>> requesting = new Dictionary<string, List<Avatar>>();
|
2021-03-31 04:47:37 -07:00
|
|
|
|
|
|
|
/// <summary>
|
2021-03-31 19:54:05 -07:00
|
|
|
/// Loaded images.
|
2021-03-31 04:47:37 -07:00
|
|
|
/// </summary>
|
2021-03-31 19:54:05 -07:00
|
|
|
private static Dictionary<string, BitmapImage> loaded = new Dictionary<string, BitmapImage>();
|
2021-03-31 04:47:37 -07:00
|
|
|
|
2021-03-31 21:03:35 -07:00
|
|
|
/// <summary>
|
|
|
|
/// Loader to join in queue.
|
|
|
|
/// </summary>
|
|
|
|
private static Task loader = null;
|
|
|
|
|
2021-03-31 04:47:37 -07:00
|
|
|
/// <summary>
|
|
|
|
/// Render implementation.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="dc"></param>
|
|
|
|
protected override void OnRender(DrawingContext dc) {
|
|
|
|
base.OnRender(dc);
|
|
|
|
|
|
|
|
if (Source == null && User != null) {
|
|
|
|
var placeholder = User.Name.Length > 0 ? User.Name.Substring(0, 1) : "?";
|
|
|
|
var formatted = new FormattedText(
|
|
|
|
placeholder,
|
|
|
|
CultureInfo.CurrentCulture,
|
|
|
|
FlowDirection.LeftToRight,
|
|
|
|
new Typeface(new FontFamily("Consolas"), FontStyles.Normal, FontWeights.Normal, FontStretches.Normal),
|
|
|
|
Width * 0.75,
|
|
|
|
Brushes.White,
|
|
|
|
VisualTreeHelper.GetDpi(this).PixelsPerDip);
|
|
|
|
|
|
|
|
double offsetX = 0;
|
|
|
|
if (HorizontalAlignment == HorizontalAlignment.Right) {
|
|
|
|
offsetX = -Width * 0.5;
|
|
|
|
}
|
|
|
|
|
|
|
|
var chars = placeholder.ToCharArray();
|
|
|
|
var sum = 0;
|
|
|
|
foreach (var ch in chars) sum += Math.Abs(ch);
|
|
|
|
var brush = Colors[sum % Colors.Length];
|
|
|
|
|
|
|
|
dc.DrawRoundedRectangle(brush, null, new Rect(-Width * 0.5 + offsetX, -Height * 0.5, Width, Height), Width / 16, Height / 16);
|
|
|
|
dc.DrawText(formatted, new Point(formatted.Width * -0.5 + offsetX, formatted.Height * -0.5));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Reset image.
|
|
|
|
/// </summary>
|
|
|
|
private void ReloadImage(Git.User oldUser) {
|
|
|
|
if (oldUser != null && requesting.ContainsKey(oldUser.Email)) {
|
2021-03-31 19:54:05 -07:00
|
|
|
if (requesting[oldUser.Email].Count <= 1) {
|
2021-03-31 04:47:37 -07:00
|
|
|
requesting.Remove(oldUser.Email);
|
|
|
|
} else {
|
2021-03-31 19:54:05 -07:00
|
|
|
requesting[oldUser.Email].Remove(this);
|
2021-03-31 04:47:37 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Source = null;
|
|
|
|
InvalidateVisual();
|
|
|
|
|
|
|
|
if (User == null) return;
|
|
|
|
|
|
|
|
var email = User.Email;
|
2021-03-31 19:54:05 -07:00
|
|
|
if (loaded.ContainsKey(email)) {
|
|
|
|
Source = loaded[email];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-03-31 04:47:37 -07:00
|
|
|
if (requesting.ContainsKey(email)) {
|
2021-03-31 19:54:05 -07:00
|
|
|
requesting[email].Add(this);
|
2021-03-31 04:47:37 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
byte[] hash = MD5.Create().ComputeHash(Encoding.Default.GetBytes(email.ToLower().Trim()));
|
|
|
|
string md5 = "";
|
|
|
|
for (int i = 0; i < hash.Length; i++) md5 += hash[i].ToString("x2");
|
|
|
|
md5 = md5.ToLower();
|
|
|
|
|
|
|
|
string filePath = Path.Combine(CACHE_PATH, md5);
|
|
|
|
if (File.Exists(filePath)) {
|
2021-03-31 19:54:05 -07:00
|
|
|
var img = new BitmapImage(new Uri(filePath));
|
|
|
|
loaded.Add(email, img);
|
|
|
|
Source = img;
|
2021-03-31 04:47:37 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-03-31 19:54:05 -07:00
|
|
|
requesting.Add(email, new List<Avatar>());
|
|
|
|
requesting[email].Add(this);
|
2021-03-31 04:47:37 -07:00
|
|
|
|
2021-03-31 21:03:35 -07:00
|
|
|
Action job = () => {
|
2021-03-31 19:54:05 -07:00
|
|
|
try {
|
2021-03-31 21:47:01 -07:00
|
|
|
HttpWebRequest req = WebRequest.CreateHttp(App.Setting.UI.AvatarServer + md5 + "?d=404");
|
2021-03-31 21:03:35 -07:00
|
|
|
req.Timeout = 2000;
|
|
|
|
req.Method = "GET";
|
|
|
|
|
|
|
|
HttpWebResponse rsp = req.GetResponse() as HttpWebResponse;
|
|
|
|
if (rsp.StatusCode == HttpStatusCode.OK) {
|
|
|
|
using (Stream reader = rsp.GetResponseStream())
|
|
|
|
using (FileStream writer = File.OpenWrite(filePath)) {
|
|
|
|
reader.CopyTo(writer);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (requesting.ContainsKey(email)) {
|
|
|
|
Dispatcher.Invoke(() => {
|
|
|
|
var img = new BitmapImage(new Uri(filePath));
|
|
|
|
loaded[email] = img;
|
|
|
|
foreach (var one in requesting[email]) one.Source = img;
|
|
|
|
});
|
|
|
|
}
|
2021-03-31 04:47:37 -07:00
|
|
|
}
|
2021-03-31 21:03:35 -07:00
|
|
|
} catch { }
|
|
|
|
|
|
|
|
requesting.Remove(email);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (loader != null && !loader.IsCompleted) {
|
|
|
|
loader = loader.ContinueWith(t => { job(); });
|
|
|
|
} else {
|
|
|
|
loader = Task.Run(job);
|
|
|
|
}
|
2021-03-31 04:47:37 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Callback on user property changed
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="d"></param>
|
|
|
|
/// <param name="e"></param>
|
|
|
|
private static void OnUserChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
|
|
|
|
Avatar a = d as Avatar;
|
|
|
|
if (a != null) a.ReloadImage(e.OldValue as Git.User);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|