2024-03-17 18:37:06 -07:00
|
|
|
|
using System;
|
2024-02-05 23:08:37 -08:00
|
|
|
|
using System.Globalization;
|
|
|
|
|
using System.Security.Cryptography;
|
|
|
|
|
using System.Text;
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
using Avalonia;
|
|
|
|
|
using Avalonia.Controls;
|
|
|
|
|
using Avalonia.Interactivity;
|
|
|
|
|
using Avalonia.Media;
|
|
|
|
|
|
|
|
|
|
namespace SourceGit.Views
|
|
|
|
|
{
|
|
|
|
|
public class Avatar : Control, Models.IAvatarHost
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
private static readonly GradientStops[] FALLBACK_GRADIENTS = [
|
|
|
|
|
new GradientStops() { new GradientStop(Colors.Orange, 0), new GradientStop(Color.FromRgb(255, 213, 134), 1) },
|
|
|
|
|
new GradientStops() { new GradientStop(Colors.DodgerBlue, 0), new GradientStop(Colors.LightSkyBlue, 1) },
|
|
|
|
|
new GradientStops() { new GradientStop(Colors.LimeGreen, 0), new GradientStop(Color.FromRgb(124, 241, 124), 1) },
|
|
|
|
|
new GradientStops() { new GradientStop(Colors.Orchid, 0), new GradientStop(Color.FromRgb(248, 161, 245), 1) },
|
|
|
|
|
new GradientStops() { new GradientStop(Colors.Tomato, 0), new GradientStop(Color.FromRgb(252, 165, 150), 1) },
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
public static readonly StyledProperty<Models.User> UserProperty =
|
|
|
|
|
AvaloniaProperty.Register<Avatar, Models.User>(nameof(User));
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
public Models.User User
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
get => GetValue(UserProperty);
|
|
|
|
|
set => SetValue(UserProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
static Avatar()
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
UserProperty.Changed.AddClassHandler<Avatar>(OnUserPropertyChanged);
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
public Avatar()
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
var refetch = new MenuItem() { Header = App.Text("RefetchAvatar") };
|
2024-03-17 18:37:06 -07:00
|
|
|
|
refetch.Click += (o, e) =>
|
|
|
|
|
{
|
|
|
|
|
if (User != null)
|
|
|
|
|
{
|
2024-02-22 19:39:05 -08:00
|
|
|
|
Models.AvatarManager.Request(_emailMD5, true);
|
2024-02-05 23:08:37 -08:00
|
|
|
|
InvalidateVisual();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ContextMenu = new ContextMenu();
|
|
|
|
|
ContextMenu.Items.Add(refetch);
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
public override void Render(DrawingContext context)
|
|
|
|
|
{
|
2024-03-31 01:54:29 -07:00
|
|
|
|
if (User == null)
|
|
|
|
|
return;
|
2024-02-05 23:08:37 -08:00
|
|
|
|
|
2024-02-22 19:39:05 -08:00
|
|
|
|
var corner = (float)Math.Max(2, Bounds.Width / 16);
|
|
|
|
|
var img = Models.AvatarManager.Request(_emailMD5, false);
|
2024-03-17 18:37:06 -07:00
|
|
|
|
if (img != null)
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
var rect = new Rect(0, 0, Bounds.Width, Bounds.Height);
|
|
|
|
|
context.PushClip(new RoundedRect(rect, corner));
|
2024-02-22 19:39:05 -08:00
|
|
|
|
context.DrawImage(img, rect);
|
2024-03-17 18:37:06 -07:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
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);
|
|
|
|
|
context.DrawText(_fallbackLabel, textOrigin);
|
2024-03-17 18:37:06 -07:00
|
|
|
|
}
|
2024-02-05 23:08:37 -08:00
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
public void OnAvatarResourceChanged(string md5)
|
|
|
|
|
{
|
|
|
|
|
if (_emailMD5 == md5)
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
InvalidateVisual();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
protected override void OnLoaded(RoutedEventArgs e)
|
|
|
|
|
{
|
2024-02-22 19:39:05 -08:00
|
|
|
|
base.OnLoaded(e);
|
|
|
|
|
Models.AvatarManager.Subscribe(this);
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
protected override void OnUnloaded(RoutedEventArgs e)
|
|
|
|
|
{
|
2024-02-22 19:39:05 -08:00
|
|
|
|
base.OnUnloaded(e);
|
|
|
|
|
Models.AvatarManager.Unsubscribe(this);
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
private static void OnUserPropertyChanged(Avatar avatar, AvaloniaPropertyChangedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (avatar.User == null)
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
avatar._emailMD5 = null;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var placeholder = string.IsNullOrWhiteSpace(avatar.User.Name) ? "?" : avatar.User.Name.Substring(0, 1);
|
|
|
|
|
var chars = placeholder.ToCharArray();
|
|
|
|
|
var sum = 0;
|
2024-03-31 01:54:29 -07:00
|
|
|
|
foreach (var c in chars)
|
|
|
|
|
sum += Math.Abs(c);
|
2024-02-05 23:08:37 -08:00
|
|
|
|
|
2024-03-31 01:54:29 -07:00
|
|
|
|
var lowered = avatar.User.Email.ToLower(CultureInfo.CurrentCulture).Trim();
|
|
|
|
|
var hash = MD5.Create().ComputeHash(Encoding.Default.GetBytes(lowered));
|
2024-02-05 23:08:37 -08:00
|
|
|
|
var builder = new StringBuilder();
|
2024-03-31 01:54:29 -07:00
|
|
|
|
foreach (var c in hash)
|
|
|
|
|
builder.Append(c.ToString("x2"));
|
2024-02-05 23:08:37 -08:00
|
|
|
|
var md5 = builder.ToString();
|
2024-03-31 01:54:29 -07:00
|
|
|
|
if (avatar._emailMD5 != md5)
|
|
|
|
|
avatar._emailMD5 = md5;
|
2024-02-05 23:08:37 -08:00
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
avatar._fallbackBrush = new LinearGradientBrush
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
GradientStops = FALLBACK_GRADIENTS[sum % FALLBACK_GRADIENTS.Length],
|
|
|
|
|
StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative),
|
|
|
|
|
EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative),
|
|
|
|
|
};
|
|
|
|
|
|
2024-03-04 23:53:38 -08:00
|
|
|
|
var typeface = new Typeface("fonts:SourceGit#JetBrains Mono");
|
2024-02-05 23:08:37 -08:00
|
|
|
|
|
|
|
|
|
avatar._fallbackLabel = new FormattedText(
|
2024-03-17 18:37:06 -07:00
|
|
|
|
placeholder,
|
|
|
|
|
CultureInfo.CurrentCulture,
|
2024-02-05 23:08:37 -08:00
|
|
|
|
FlowDirection.LeftToRight,
|
|
|
|
|
typeface,
|
|
|
|
|
avatar.Width * 0.65,
|
|
|
|
|
Brushes.White);
|
|
|
|
|
|
|
|
|
|
avatar.InvalidateVisual();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private FormattedText _fallbackLabel = null;
|
|
|
|
|
private LinearGradientBrush _fallbackBrush = null;
|
|
|
|
|
private string _emailMD5 = null;
|
|
|
|
|
}
|
2024-03-31 01:54:29 -07:00
|
|
|
|
}
|