11using System ;
22using System . Diagnostics . CodeAnalysis ;
3+ using System . Net ;
34using System . Reactive ;
45using System . Reactive . Linq ;
56using System . Reactive . Threading . Tasks ;
@@ -23,6 +24,7 @@ namespace GitHub.ViewModels
2324 public abstract class LoginTabViewModel : ReactiveObject
2425 {
2526 static readonly ILogger log = LogManager . ForContext < LoginTabViewModel > ( ) ;
27+ CancellationTokenSource oauthCancel ;
2628
2729 protected LoginTabViewModel (
2830 IConnectionManager connectionManager ,
@@ -46,25 +48,12 @@ protected LoginTabViewModel(
4648 ( x , y ) => x . Value && y . Value ) . ToProperty ( this , x => x . CanLogin ) ;
4749
4850 Login = ReactiveCommand . CreateAsyncObservable ( this . WhenAny ( x => x . CanLogin , x => x . Value ) , LogIn ) ;
49-
50- Login . ThrownExceptions . Subscribe ( ex =>
51- {
52- if ( ex . IsCriticalException ( ) ) return ;
53-
54- log . Information ( ex , "Error logging into '{BaseUri}' as '{UsernameOrEmail}'" , BaseUri , UsernameOrEmail ) ;
55- if ( ex is Octokit . ForbiddenException )
56- {
57- Error = new UserError ( Resources . LoginFailedForbiddenMessage , ex . Message ) ;
58- }
59- else
60- {
61- Error = new UserError ( ex . Message ) ;
62- }
63- } ) ;
51+ Login . ThrownExceptions . Subscribe ( HandleError ) ;
6452
6553 LoginViaOAuth = ReactiveCommand . CreateAsyncTask (
6654 this . WhenAnyValue ( x => x . IsLoggingIn , x => ! x ) ,
6755 LogInViaOAuth ) ;
56+ LoginViaOAuth . ThrownExceptions . Subscribe ( HandleError ) ;
6857
6958 isLoggingIn = Login . IsExecuting . ToProperty ( this , x => x . IsLoggingIn ) ;
7059
@@ -146,6 +135,8 @@ public UserError Error
146135 set { this . RaiseAndSetIfChanged ( ref error , value ) ; }
147136 }
148137
138+ public void Deactivated ( ) => oauthCancel ? . Cancel ( ) ;
139+
149140 protected abstract IObservable < AuthenticationResult > LogIn ( object args ) ;
150141 protected abstract Task < AuthenticationResult > LogInViaOAuth ( object args ) ;
151142
@@ -200,13 +191,16 @@ protected IObservable<AuthenticationResult> LogInToHost(HostAddress hostAddress)
200191
201192 protected async Task LoginToHostViaOAuth ( HostAddress address )
202193 {
194+ oauthCancel = new CancellationTokenSource ( ) ;
195+
203196 try
204197 {
205- await ConnectionManager . LogInViaOAuth ( address , CancellationToken . None ) ;
198+ await ConnectionManager . LogInViaOAuth ( address , oauthCancel . Token ) ;
206199 }
207- catch ( Exception e )
200+ finally
208201 {
209- Error = new UserError ( e . Message ) ;
202+ oauthCancel . Dispose ( ) ;
203+ oauthCancel = null ;
210204 }
211205 }
212206
@@ -224,5 +218,32 @@ protected virtual Task ResetValidation()
224218 // noop
225219 return Task . FromResult ( 0 ) ;
226220 }
221+
222+ void HandleError ( Exception ex )
223+ {
224+ // The Windows ERROR_OPERATION_ABORTED error code.
225+ const int operationAborted = 995 ;
226+
227+ if ( ex is HttpListenerException &&
228+ ( ( HttpListenerException ) ex ) . ErrorCode == operationAborted )
229+ {
230+ // An Oauth listener was aborted, probably because the user closed the login
231+ // dialog or switched between the GitHub and Enterprise tabs while listening
232+ // for an Oauth callbacl.
233+ return ;
234+ }
235+
236+ if ( ex . IsCriticalException ( ) ) return ;
237+
238+ log . Information ( ex , "Error logging into '{BaseUri}' as '{UsernameOrEmail}'" , BaseUri , UsernameOrEmail ) ;
239+ if ( ex is Octokit . ForbiddenException )
240+ {
241+ Error = new UserError ( Resources . LoginFailedForbiddenMessage , ex . Message ) ;
242+ }
243+ else
244+ {
245+ Error = new UserError ( ex . Message ) ;
246+ }
247+ }
227248 }
228249}
0 commit comments