11using System ;
22using System . Collections . Generic ;
3+ using System . Globalization ;
34using System . IO ;
45using System . Net . Http ;
6+ using System . Security . Cryptography ;
7+ using System . Text ;
8+ using System . Text . RegularExpressions ;
59using System . Threading ;
610using System . Threading . Tasks ;
711
812using Avalonia . Media . Imaging ;
13+ using Avalonia . Platform ;
914using Avalonia . Threading ;
1015
1116namespace SourceGit . Models
1217{
1318 public interface IAvatarHost
1419 {
15- void OnAvatarResourceChanged ( string md5 ) ;
20+ void OnAvatarResourceChanged ( string email ) ;
1621 }
1722
18- public static class AvatarManager
23+ public static partial class AvatarManager
1924 {
2025 public static string SelectedServer
2126 {
@@ -33,29 +38,35 @@ static AvatarManager()
3338 {
3439 while ( true )
3540 {
36- var md5 = null as string ;
41+ var email = null as string ;
3742
3843 lock ( _synclock )
3944 {
4045 foreach ( var one in _requesting )
4146 {
42- md5 = one ;
47+ email = one ;
4348 break ;
4449 }
4550 }
4651
47- if ( md5 == null )
52+ if ( email == null )
4853 {
4954 Thread . Sleep ( 100 ) ;
5055 continue ;
5156 }
5257
58+ var md5 = GetEmailHash ( email ) ;
59+ var matchGithubUser = REG_GITHUB_USER_EMAIL ( ) . Match ( email ) ;
60+ var url = matchGithubUser . Success ?
61+ $ "https://avatars.githubusercontent.com/{ matchGithubUser . Groups [ 2 ] . Value } " :
62+ $ "{ SelectedServer } { md5 } ?d=404";
63+
5364 var localFile = Path . Combine ( _storePath , md5 ) ;
5465 var img = null as Bitmap ;
5566 try
5667 {
5768 var client = new HttpClient ( ) { Timeout = TimeSpan . FromSeconds ( 2 ) } ;
58- var task = client . GetAsync ( $ " { SelectedServer } { md5 } ?d=404" ) ;
69+ var task = client . GetAsync ( url ) ;
5970 task . Wait ( ) ;
6071
6172 var rsp = task . Result ;
@@ -82,13 +93,13 @@ static AvatarManager()
8293
8394 lock ( _synclock )
8495 {
85- _requesting . Remove ( md5 ) ;
96+ _requesting . Remove ( email ) ;
8697 }
8798
8899 Dispatcher . UIThread . InvokeAsync ( ( ) =>
89100 {
90- _resources [ md5 ] = img ;
91- NotifyResourceChanged ( md5 ) ;
101+ _resources [ email ] = img ;
102+ NotifyResourceChanged ( email ) ;
92103 } ) ;
93104 }
94105 } ) ;
@@ -104,33 +115,44 @@ public static void Unsubscribe(IAvatarHost host)
104115 _avatars . Remove ( host ) ;
105116 }
106117
107- public static Bitmap Request ( string md5 , bool forceRefetch = false )
118+ public static Bitmap Request ( string email , bool forceRefetch )
108119 {
120+ if ( email . Equals ( "[email protected] " , StringComparison . Ordinal ) ) 121+ {
122+ if ( _githubEmailAvatar == null )
123+ {
124+ var icon = AssetLoader . Open ( new Uri ( $ "avares://SourceGit/Resources/Images/github.png", UriKind . RelativeOrAbsolute ) ) ;
125+ _githubEmailAvatar = new Bitmap ( icon ) ;
126+ }
127+
128+ return _githubEmailAvatar ;
129+ }
130+
109131 if ( forceRefetch )
110132 {
111- if ( _resources . ContainsKey ( md5 ) )
112- _resources . Remove ( md5 ) ;
133+ if ( _resources . ContainsKey ( email ) )
134+ _resources . Remove ( email ) ;
113135
114- var localFile = Path . Combine ( _storePath , md5 ) ;
136+ var localFile = Path . Combine ( _storePath , GetEmailHash ( email ) ) ;
115137 if ( File . Exists ( localFile ) )
116138 File . Delete ( localFile ) ;
117139
118- NotifyResourceChanged ( md5 ) ;
140+ NotifyResourceChanged ( email ) ;
119141 }
120142 else
121143 {
122- if ( _resources . TryGetValue ( md5 , out var value ) )
144+ if ( _resources . TryGetValue ( email , out var value ) )
123145 return value ;
124146
125- var localFile = Path . Combine ( _storePath , md5 ) ;
147+ var localFile = Path . Combine ( _storePath , GetEmailHash ( email ) ) ;
126148 if ( File . Exists ( localFile ) )
127149 {
128150 try
129151 {
130152 using ( var stream = File . OpenRead ( localFile ) )
131153 {
132154 var img = Bitmap . DecodeToWidth ( stream , 128 ) ;
133- _resources . Add ( md5 , img ) ;
155+ _resources . Add ( email , img ) ;
134156 return img ;
135157 }
136158 }
@@ -143,18 +165,28 @@ public static Bitmap Request(string md5, bool forceRefetch = false)
143165
144166 lock ( _synclock )
145167 {
146- if ( ! _requesting . Contains ( md5 ) )
147- _requesting . Add ( md5 ) ;
168+ if ( ! _requesting . Contains ( email ) )
169+ _requesting . Add ( email ) ;
148170 }
149171
150172 return null ;
151173 }
152174
153- private static void NotifyResourceChanged ( string md5 )
175+ private static string GetEmailHash ( string email )
176+ {
177+ var lowered = email . ToLower ( CultureInfo . CurrentCulture ) . Trim ( ) ;
178+ var hash = MD5 . Create ( ) . ComputeHash ( Encoding . Default . GetBytes ( lowered ) ) ;
179+ var builder = new StringBuilder ( ) ;
180+ foreach ( var c in hash )
181+ builder . Append ( c . ToString ( "x2" ) ) ;
182+ return builder . ToString ( ) ;
183+ }
184+
185+ private static void NotifyResourceChanged ( string email )
154186 {
155187 foreach ( var avatar in _avatars )
156188 {
157- avatar . OnAvatarResourceChanged ( md5 ) ;
189+ avatar . OnAvatarResourceChanged ( email ) ;
158190 }
159191 }
160192
@@ -163,5 +195,9 @@ private static void NotifyResourceChanged(string md5)
163195 private static readonly List < IAvatarHost > _avatars = new List < IAvatarHost > ( ) ;
164196 private static readonly Dictionary < string , Bitmap > _resources = new Dictionary < string , Bitmap > ( ) ;
165197 private static readonly HashSet < string > _requesting = new HashSet < string > ( ) ;
198+
199+ [ GeneratedRegex ( @"^(?:(\d+)\+)?(.+?)@users\.noreply\.github\.com$" ) ]
200+ private static partial Regex REG_GITHUB_USER_EMAIL ( ) ;
201+ private static Bitmap _githubEmailAvatar = null ;
166202 }
167203}
0 commit comments