Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Graph;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media.Imaging;

namespace Microsoft.Toolkit.Graph.Controls
{
/// <summary>
/// The <see cref="PersonView"/> control displays a user photo and can display their name and e-mail.
/// </summary>
public partial class PersonView
{
/// <summary>
/// Gets or sets details about this person retrieved from the graph or provided by the developer.
/// </summary>
public Person PersonDetails
{
get { return (Person)GetValue(PersonDetailsProperty); }
set { SetValue(PersonDetailsProperty, value); }
}

/// <summary>
/// Identifies the <see cref="PersonDetails"/> dependency property.
/// </summary>
/// <returns>
/// The identifier for the <see cref="PersonDetails"/> dependency property.
/// </returns>
public static readonly DependencyProperty PersonDetailsProperty =
DependencyProperty.Register(nameof(PersonDetails), typeof(Person), typeof(PersonView), new PropertyMetadata(null, PersonDetailsPropertyChanged));

/// <summary>
/// Gets or sets a string to automatically retrieve data on the specified query from the graph. Use <see cref="PersonQueryMe"/> to retrieve info about the current user. Otherwise, it's best to use an e-mail address as a query.
/// </summary>
public string PersonQuery
{
get { return (string)GetValue(PersonQueryProperty); }
set { SetValue(PersonQueryProperty, value); }
}

/// <summary>
/// Identifies the <see cref="PersonQuery"/> dependency property.
/// </summary>
/// <returns>
/// The identifier for the <see cref="PersonQuery"/> dependency property.
/// </returns>
public static readonly DependencyProperty PersonQueryProperty =
DependencyProperty.Register(nameof(PersonQuery), typeof(string), typeof(PersonView), new PropertyMetadata(null, QueryPropertyChanged));

/// <summary>
/// Gets or sets the UserId.
/// </summary>
public string UserId
{
get { return (string)GetValue(UserIdProperty); }
set { SetValue(UserIdProperty, value); }
}

/// <summary>
/// Identifies the <see cref="UserId"/> dependency property.
/// </summary>
/// <returns>
/// The identifier for the <see cref="UserId"/> dependency property.
/// </returns>
public static readonly DependencyProperty UserIdProperty =
DependencyProperty.Register(nameof(UserId), typeof(string), typeof(PersonView), new PropertyMetadata(null, QueryPropertyChanged));

/// <summary>
/// Gets or sets a value indicating whether the user's name should be displayed.
/// </summary>
public bool ShowName
{
get { return (bool)GetValue(ShowNameProperty); }
set { SetValue(ShowNameProperty, value); }
}

/// <summary>
/// Identifies the <see cref="ShowName"/> dependency property.
/// </summary>
/// <returns>
/// The identifier for the <see cref="ShowName"/> dependency property.
/// </returns>
public static readonly DependencyProperty ShowNameProperty =
DependencyProperty.Register(nameof(ShowName), typeof(bool), typeof(PersonView), new PropertyMetadata(false, ShowDisplayPropertiesChanged));

/// <summary>
/// Gets or sets a value indicating whether the user's email address should be displayed.
/// </summary>
public bool ShowEmail
{
get { return (bool)GetValue(ShowEmailProperty); }
set { SetValue(ShowEmailProperty, value); }
}

/// <summary>
/// Identifies the <see cref="ShowEmail"/> dependency property.
/// </summary>
/// <returns>
/// The identifier for the <see cref="ShowEmail"/> dependency property.
/// </returns>
public static readonly DependencyProperty ShowEmailProperty =
DependencyProperty.Register(nameof(ShowEmail), typeof(bool), typeof(PersonView), new PropertyMetadata(false, ShowDisplayPropertiesChanged));

/// <summary>
/// Gets or sets the photo of the user to be displayed.
/// </summary>
public BitmapImage UserPhoto
{
get { return (BitmapImage)GetValue(UserPhotoProperty); }
set { SetValue(UserPhotoProperty, value); }
}

/// <summary>
/// Identifies the <see cref="UserPhoto"/> dependency property.
/// </summary>
/// <returns>
/// The identifier for the <see cref="UserPhoto"/> dependency property.
/// </returns>
public static readonly DependencyProperty UserPhotoProperty =
DependencyProperty.Register(nameof(UserPhoto), typeof(BitmapImage), typeof(PersonView), new PropertyMetadata(null));

/// <summary>
/// Gets the initials of the person from the <see cref="PersonDetails"/>.
/// </summary>
public string Initials
{
get { return (string)GetValue(InitialsProperty); }
internal set { SetValue(InitialsProperty, value); }
}

/// <summary>
/// Identifies the <see cref="Initials"/> dependency property.
/// </summary>
/// <returns>
/// The identifier for the <see cref="Initials"/> dependency property.
/// </returns>
public static readonly DependencyProperty InitialsProperty =
DependencyProperty.Register(nameof(Initials), typeof(string), typeof(PersonView), new PropertyMetadata(string.Empty));

/// <summary>
/// Gets a value indicating whether the image has expanded because both <see cref="ShowName"/> and <see cref="ShowEmail"/> are enabled.
/// </summary>
public bool IsLargeImage
{
get { return (bool)GetValue(IsLargeImageProperty); }
internal set { SetValue(IsLargeImageProperty, value); }
}

/// <summary>
/// Identifies the <see cref="IsLargeImage"/> dependency property.
/// </summary>
/// <returns>
/// The identifier for the <see cref="IsLargeImage"/> dependency property.
/// </returns>
public static readonly DependencyProperty IsLargeImageProperty =
DependencyProperty.Register(nameof(IsLargeImage), typeof(bool), typeof(PersonView), new PropertyMetadata(false));
}
}
222 changes: 222 additions & 0 deletions Microsoft.Toolkit.Graph.Controls/Controls/PersonView/PersonView.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Graph;
using Microsoft.Toolkit.Graph.Helpers;
using Microsoft.Toolkit.Graph.Providers;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Imaging;

namespace Microsoft.Toolkit.Graph.Controls
{
/// <summary>
/// The <see cref="PersonView"/> control displays a user photo and can display their name and e-mail.
/// </summary>
public partial class PersonView : Control
{
/// <summary>
/// <see cref="PersonQuery"/> value used to retrieve the signed-in user's info.
/// </summary>
public const string PersonQueryMe = "me";

private static readonly string[] RequiredScopes = new string[] { "user.readbasic.all" };

private string _photoId = null;

private static async void PersonDetailsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is PersonView pv)
{
if (pv.PersonDetails != null)
{
if (pv.PersonDetails.GivenName?.Length > 0 && pv.PersonDetails.Surname?.Length > 0)
{
pv.Initials = string.Empty + pv.PersonDetails.GivenName[0] + pv.PersonDetails.Surname[0];
}
else if (pv.PersonDetails.DisplayName?.Length > 0)
{
// Grab first two initials in name
var initials = pv.PersonDetails.DisplayName.ToUpper().Split(' ').Select(i => i.First());
pv.Initials = string.Join(string.Empty, initials.Where(i => char.IsLetter(i)).Take(2));
}

if (pv.UserPhoto == null || pv.PersonDetails.Id != pv._photoId)
{
// Reload Image
pv.UserPhoto = null;
await pv.LoadImageAsync(pv.PersonDetails);
}
else if (pv.PersonDetails.Id != pv._photoId)
{
pv.UserPhoto = null;
pv._photoId = null;
}
}
}
}

private static void QueryPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is PersonView view)
{
view.PersonDetails = null;
view.LoadData();
}
}

private static void ShowDisplayPropertiesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is PersonView pv)
{
pv.IsLargeImage = pv.ShowName && pv.ShowEmail;
}
}

/// <summary>
/// Initializes a new instance of the <see cref="PersonView"/> class.
/// </summary>
public PersonView()
{
this.DefaultStyleKey = typeof(PersonView);

ProviderManager.Instance.ProviderUpdated += (sender, args) => LoadData();
}

/// <inheritdoc/>
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();

LoadData();
}

private async void LoadData()
{
var provider = ProviderManager.Instance.GlobalProvider;

if (provider == null || provider.State != ProviderState.SignedIn)
{
return;
}

if (PersonDetails != null && UserPhoto == null)
{
await LoadImageAsync(PersonDetails);
}
else if (!string.IsNullOrWhiteSpace(UserId) || PersonQuery?.ToLowerInvariant() == PersonQueryMe)
{
User user = null;
if (!string.IsNullOrWhiteSpace(UserId))
{
// TODO: Batch when API easier https://github.com/microsoftgraph/msgraph-sdk-dotnet-core/issues/29
try
{
user = await provider.Graph.Users[UserId].Request().GetAsync();
}
catch
{
}

try
{
// TODO: Move to LoadImage based on previous call?
await DecodeStreamAsync(await provider.Graph.Users[UserId].Photo.Content.Request().GetAsync());
_photoId = UserId;
}
catch
{
}
}
else
{
try
{
user = await provider.Graph.Me.Request().GetAsync();
}
catch
{
}

try
{
await DecodeStreamAsync(await provider.Graph.Me.Photo.Content.Request().GetAsync());
_photoId = user.Id;
}
catch
{
}
}

if (user != null)
{
PersonDetails = new Person()
{
Id = user.Id,
DisplayName = user.DisplayName,
ScoredEmailAddresses = new ScoredEmailAddress[]
{
new ScoredEmailAddress()
{
Address = user.Mail ?? user.UserPrincipalName
}
},
GivenName = user.GivenName,
Surname = user.Surname
};
}
}
else if (PersonDetails == null && !string.IsNullOrWhiteSpace(PersonQuery))
{
var people = await provider.Graph.FindPersonAsync(PersonQuery);
if (people != null && people.Count > 0)
{
var person = people.FirstOrDefault();
PersonDetails = person;
await LoadImageAsync(person);
}
}
}

private async Task LoadImageAsync(Person person)
{
try
{
// TODO: Better guarding
var graph = ProviderManager.Instance.GlobalProvider.Graph;

if (!string.IsNullOrWhiteSpace(person.UserPrincipalName))
{
await DecodeStreamAsync(await graph.GetUserPhoto(person.UserPrincipalName));
_photoId = person.Id; // TODO: Only set on success for photo?
}
else if (!string.IsNullOrWhiteSpace(person.ScoredEmailAddresses.First().Address))
{
// TODO https://github.com/microsoftgraph/microsoft-graph-toolkit/blob/master/src/components/mgt-person/mgt-person.ts#L174
}
}
catch
{
// If we can't load a photo, that's ok.
}
}

private async Task DecodeStreamAsync(Stream photoStream)
{
if (photoStream != null)
{
using (var ras = photoStream.AsRandomAccessStream())
{
var bitmap = new BitmapImage();
await bitmap.SetSourceAsync(ras);
UserPhoto = bitmap;
}
}
}
}
}
Loading