diff --git a/CommunityToolkit.Graph.Uwp/Controls/LoginButton/LoginButton.Properties.cs b/CommunityToolkit.Graph.Uwp/Controls/LoginButton/LoginButton.Properties.cs index 82374bc..70b63c1 100644 --- a/CommunityToolkit.Graph.Uwp/Controls/LoginButton/LoginButton.Properties.cs +++ b/CommunityToolkit.Graph.Uwp/Controls/LoginButton/LoginButton.Properties.cs @@ -18,7 +18,7 @@ public partial class LoginButton public User UserDetails { get { return (User)GetValue(UserDetailsProperty); } - set { SetValue(UserDetailsProperty, value); } + protected set { SetValue(UserDetailsProperty, value); } } /// diff --git a/CommunityToolkit.Graph.Uwp/Controls/LoginButton/LoginButton.cs b/CommunityToolkit.Graph.Uwp/Controls/LoginButton/LoginButton.cs index b69b3d6..991d6fa 100644 --- a/CommunityToolkit.Graph.Uwp/Controls/LoginButton/LoginButton.cs +++ b/CommunityToolkit.Graph.Uwp/Controls/LoginButton/LoginButton.cs @@ -17,14 +17,14 @@ namespace CommunityToolkit.Graph.Uwp.Controls /// The control is a button which can be used to sign the user in or show them profile details. /// [TemplatePart(Name = LoginButtonPart, Type = typeof(Button))] - [TemplatePart(Name = SignOutButtonPart, Type = typeof(ButtonBase))] + [TemplatePart(Name = LogoutButtonPart, Type = typeof(ButtonBase))] public partial class LoginButton : Control { private const string LoginButtonPart = "PART_LoginButton"; - private const string SignOutButtonPart = "PART_SignOutButton"; + private const string LogoutButtonPart = "PART_LogoutButton"; private Button _loginButton; - private ButtonBase _signOutButton; + private ButtonBase _logoutButton; private bool _isLoading; @@ -48,17 +48,87 @@ public LoginButton() { this.DefaultStyleKey = typeof(LoginButton); - ProviderManager.Instance.ProviderStateChanged += (sender, args) => LoadData(); + ProviderManager.Instance.ProviderUpdated += OnProviderUpdated; + ProviderManager.Instance.ProviderStateChanged += OnProviderStateChanged; } /// - /// Update the enablement state of the button in relation to the _isLoading property. + /// Initiates logging in with the current registered in the . /// - protected void UpdateButtonEnablement() + /// A representing the asynchronous operation. + public async Task SignInAsync() { - if (_loginButton != null) + if (IsLoading) { - _loginButton.IsEnabled = !_isLoading; + return; + } + + var provider = ProviderManager.Instance.GlobalProvider; + if (provider != null) + { + try + { + IsLoading = true; + + var cargs = new CancelEventArgs(); + LoginInitiated?.Invoke(this, cargs); + if (cargs.Cancel) + { + throw new OperationCanceledException(); + } + + await provider.SignInAsync(); + + if (provider.State != ProviderState.SignedIn) + { + throw new Exception("Login did not complete."); + } + } + catch (Exception e) + { + IsLoading = false; + LoginFailed?.Invoke(this, new LoginFailedEventArgs(e)); + } + } + } + + /// + /// Log a signed-in user out. + /// + /// A representing the asynchronous operation. + public async Task SignOutAsync() + { + if (IsLoading) + { + return; + } + + IsLoading = true; + + var cargs = new CancelEventArgs(); + LogoutInitiated?.Invoke(this, cargs); + if (cargs.Cancel) + { + return; + } + + var provider = ProviderManager.Instance.GlobalProvider; + if (provider != null) + { + try + { + await provider.SignOutAsync(); + + if (provider.State != ProviderState.SignedOut) + { + throw new Exception("Logout did not complete."); + } + } + finally + { + // There is no LogoutFailed event, so we do nothing. + IsLoading = false; + } } } @@ -79,171 +149,155 @@ protected override void OnApplyTemplate() _loginButton.Click += LoginButton_Click; } - if (_signOutButton != null) + if (_logoutButton != null) { - _signOutButton.Click -= LoginButton_Click; + _logoutButton.Click -= LogoutButton_Click; } - _signOutButton = GetTemplateChild(SignOutButtonPart) as ButtonBase; + _logoutButton = GetTemplateChild(LogoutButtonPart) as ButtonBase; - if (_signOutButton != null) + if (_logoutButton != null) { - _signOutButton.Click += SignOutButton_Click; + _logoutButton.Click += LogoutButton_Click; } LoadData(); } - private async void LoginButton_Click(object sender, RoutedEventArgs e) + /// + /// Show the user details flyout. + /// + protected void ShowFlyout() { - if (this.UserDetails != null) + if (FlyoutBase.GetAttachedFlyout(_loginButton) is FlyoutBase flyout) { - if (FlyoutBase.GetAttachedFlyout(_loginButton) is FlyoutBase flyout) - { - flyout.ShowAt(_loginButton); - } + flyout.ShowAt(_loginButton); } - else + } + + /// + /// Hide the user details flyout. + /// + protected void HideFlyout() + { + if (FlyoutBase.GetAttachedFlyout(_loginButton) is FlyoutBase flyout) { - var cargs = new CancelEventArgs(); - LoginInitiated?.Invoke(this, cargs); + flyout.Hide(); + } + } - if (!cargs.Cancel) - { - await SignInAsync(); - } + /// + /// Update the enablement state of the button in relation to the _isLoading property. + /// + protected void UpdateButtonEnablement() + { + if (_loginButton != null) + { + _loginButton.IsEnabled = !_isLoading; } } - private async void SignOutButton_Click(object sender, RoutedEventArgs e) + private void OnProviderUpdated(object sender, IProvider e) { - await SignOutAsync(); + if (e == null) + { + ClearUserDetails(); + } } - private async void LoadData() + private async void OnProviderStateChanged(object sender, ProviderStateChangedEventArgs e) { var provider = ProviderManager.Instance.GlobalProvider; - switch (provider?.State) + switch (provider.State) { - case ProviderState.Loading: + case ProviderState.SignedIn: IsLoading = true; + await SetUserDetails(); + IsLoading = false; + LoginCompleted?.Invoke(this, new EventArgs()); break; - case ProviderState.SignedIn: - try - { - IsLoading = true; + case ProviderState.SignedOut: + ClearUserDetails(); + IsLoading = false; + LogoutCompleted?.Invoke(this, new EventArgs()); + break; - // https://github.com/microsoftgraph/microsoft-graph-toolkit/blob/master/src/components/mgt-login/mgt-login.ts#L139 - // TODO: Batch with photo request later? https://github.com/microsoftgraph/msgraph-sdk-dotnet-core/issues/29 - UserDetails = await provider.GetClient().GetMeAsync(); - } - catch (Exception e) - { - LoginFailed?.Invoke(this, new LoginFailedEventArgs(e)); - } - finally - { - IsLoading = false; - } + case ProviderState.Loading: + IsLoading = true; + break; + } + } + private async void LoginButton_Click(object sender, RoutedEventArgs e) + { + var provider = ProviderManager.Instance.GlobalProvider; + switch (provider.State) + { + case ProviderState.SignedIn: + ShowFlyout(); break; case ProviderState.SignedOut: - default: - UserDetails = null; // What if this was user provided? Should we not hook into these events then? - IsLoading = false; + await SignInAsync(); break; } } - /// - /// Initiates logging in with the current registered in the . - /// - /// A representing the asynchronous operation. - public async Task SignInAsync() + private async void LogoutButton_Click(object sender, RoutedEventArgs e) + { + HideFlyout(); + await SignOutAsync(); + } + + private async void LoadData() { - if (UserDetails != null || IsLoading) + if (IsLoading) { return; } var provider = ProviderManager.Instance.GlobalProvider; - - if (provider != null) + switch (provider?.State) { - try - { + case ProviderState.Loading: IsLoading = true; - await provider.SignInAsync(); + break; - if (provider.State == ProviderState.SignedIn) + case ProviderState.SignedIn: + if (UserDetails == null) { - // TODO: include user details? - LoginCompleted?.Invoke(this, new EventArgs()); - - LoadData(); + await SignInAsync(); } - else + + IsLoading = false; + break; + + case ProviderState.SignedOut: + if (UserDetails != null) { - LoginFailed?.Invoke(this, new LoginFailedEventArgs(new TimeoutException("Login did not complete."))); + await SignOutAsync(); } - } - catch (Exception e) - { - LoginFailed?.Invoke(this, new LoginFailedEventArgs(e)); - } - finally - { + IsLoading = false; - } + break; } } - /// - /// Log a signed-in user out. - /// - /// A representing the asynchronous operation. - public async Task SignOutAsync() + private async Task SetUserDetails() { - // Close Menu - if (FlyoutBase.GetAttachedFlyout(_loginButton) is FlyoutBase flyout) - { - flyout.Hide(); - } - - if (IsLoading) - { - return; - } - - var cargs = new CancelEventArgs(); - LogoutInitiated?.Invoke(this, cargs); - - if (cargs.Cancel) - { - return; - } - - if (UserDetails != null) - { - UserDetails = null; - } - else - { - return; // No-op - } - var provider = ProviderManager.Instance.GlobalProvider; - if (provider != null) { - IsLoading = true; - await provider.SignOutAsync(); - IsLoading = false; - - LogoutCompleted?.Invoke(this, new EventArgs()); + // https://github.com/microsoftgraph/microsoft-graph-toolkit/blob/master/src/components/mgt-login/mgt-login.ts#L139 + // TODO: Batch with photo request later? https://github.com/microsoftgraph/msgraph-sdk-dotnet-core/issues/29 + UserDetails = await provider.GetClient().GetMeAsync(); } } + + private void ClearUserDetails() + { + UserDetails = null; + } } } diff --git a/CommunityToolkit.Graph.Uwp/Controls/LoginButton/LoginButton.xaml b/CommunityToolkit.Graph.Uwp/Controls/LoginButton/LoginButton.xaml index acbae71..eb5e6fb 100644 --- a/CommunityToolkit.Graph.Uwp/Controls/LoginButton/LoginButton.xaml +++ b/CommunityToolkit.Graph.Uwp/Controls/LoginButton/LoginButton.xaml @@ -45,7 +45,7 @@ - +