From 89883411574c72328d01bcd5de0464d99957d3f4 Mon Sep 17 00:00:00 2001 From: michael-hawker Date: Sun, 20 Oct 2019 01:04:45 -0700 Subject: [PATCH 01/22] Initial GraphPresenter prototype for #17 --- .../Controls/GraphPresenter/GraphPresenter.cs | 78 ++++++++ .../Converters/FileSizeConverter.cs | 24 +++ .../Converters/OneDriveThumbnailConverter.cs | 35 ++++ .../Providers/NotifyTaskCompletion.cs | 170 ++++++++++++++++++ .../Extensions/GraphExtensions.cs | 53 ++++++ .../Providers/ProviderManager.cs | 8 +- SampleTest/MainPage.xaml | 62 +++++-- 7 files changed, 417 insertions(+), 13 deletions(-) create mode 100644 Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/GraphPresenter.cs create mode 100644 Microsoft.Toolkit.Graph.Controls/Converters/FileSizeConverter.cs create mode 100644 Microsoft.Toolkit.Graph.Controls/Converters/OneDriveThumbnailConverter.cs create mode 100644 Microsoft.Toolkit.Graph.Controls/Providers/NotifyTaskCompletion.cs diff --git a/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/GraphPresenter.cs b/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/GraphPresenter.cs new file mode 100644 index 0000000..ad37c23 --- /dev/null +++ b/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/GraphPresenter.cs @@ -0,0 +1,78 @@ +using Microsoft.Graph; +using Microsoft.Toolkit.Graph.Providers; +using Microsoft.Toolkit.Uwp.Helpers; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; + +namespace Microsoft.Toolkit.Graph.Controls +{ + /// + /// Specialized to fetch and display data from the Microsoft Graph. + /// + public class GraphPresenter : ContentPresenter + { + /// + /// Gets or sets a to be used to make a request to the graph. The results will be automatically populated to the property. Use a to change the presentation of the data. + /// + public object RequestBuilder + { + get { return (object)GetValue(RequestBuilderProperty); } + set { SetValue(RequestBuilderProperty, value); } + } + + /// + /// Identifies the dependency property. + /// + /// + /// The identifier for the dependency property. + /// + public static readonly DependencyProperty RequestBuilderProperty = + DependencyProperty.Register(nameof(RequestBuilder), typeof(object), typeof(GraphPresenter), new PropertyMetadata(null, OnDataChanged)); + + public Type ResponseType { get; set; } + + public bool IsCollection { get; set; } + + private static async void OnDataChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is GraphPresenter presenter) + { + // TODO: Should this be a Graph SDK bug? The interfaces don't match the similar class hierarchy? + if (e.NewValue is BaseRequestBuilder builder) + { + var request = new BaseRequest(builder.RequestUrl, builder.Client, null); + request.Method = "GET"; + + var response = await request.SendAsync(null, CancellationToken.None).ConfigureAwait(false) as JObject; + + //// TODO: Deal with paging? + + var values = response["value"]; + object data = null; + + if (presenter.IsCollection) + { + data = values.ToObject(Array.CreateInstance(presenter.ResponseType, 0).GetType()); + } + else + { + data = values.ToObject(presenter.ResponseType); + } + + await DispatcherHelper.ExecuteOnUIThreadAsync(() => + { + presenter.Content = data; + }); + } + } + } + } +} diff --git a/Microsoft.Toolkit.Graph.Controls/Converters/FileSizeConverter.cs b/Microsoft.Toolkit.Graph.Controls/Converters/FileSizeConverter.cs new file mode 100644 index 0000000..bff2d95 --- /dev/null +++ b/Microsoft.Toolkit.Graph.Controls/Converters/FileSizeConverter.cs @@ -0,0 +1,24 @@ +using System; +using Microsoft.Toolkit.Graph.Extensions; +using Windows.UI.Xaml.Data; + +namespace Microsoft.Toolkit.Graph.Controls.Converters +{ + public class FileSizeConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, string language) + { + if (value is long size) + { + return size.ToFileSizeString(); + } + + return null; + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + throw new NotImplementedException(); + } + } +} diff --git a/Microsoft.Toolkit.Graph.Controls/Converters/OneDriveThumbnailConverter.cs b/Microsoft.Toolkit.Graph.Controls/Converters/OneDriveThumbnailConverter.cs new file mode 100644 index 0000000..68b3c85 --- /dev/null +++ b/Microsoft.Toolkit.Graph.Controls/Converters/OneDriveThumbnailConverter.cs @@ -0,0 +1,35 @@ +using System; +using Microsoft.Graph; +using Microsoft.Toolkit.Graph.Providers; +using Windows.UI.Xaml.Data; + +namespace Microsoft.Toolkit.Graph.Controls.Converters +{ + /// + /// Helper to return a . + /// + public class OneDriveThumbnailConverter : IValueConverter + { + /// + public object Convert(object value, Type targetType, object parameter, string language) + { + if (value is RemoteItem ri) + { + // drives/${file.remoteItem.parentReference.driveId}/items/${file.remoteItem.id}/thumbnails/0/medium + var provider = ProviderManager.Instance.GlobalProvider; + if (provider != null && provider.Graph != null) + { + return new NotifyTaskCompletion(provider.Graph.Drives[ri.ParentReference.DriveId].Items[ri.Id].Thumbnails["0"].Request().GetAsync()); + } + } + + return null; + } + + /// + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + throw new NotImplementedException(); + } + } +} diff --git a/Microsoft.Toolkit.Graph.Controls/Providers/NotifyTaskCompletion.cs b/Microsoft.Toolkit.Graph.Controls/Providers/NotifyTaskCompletion.cs new file mode 100644 index 0000000..09091b0 --- /dev/null +++ b/Microsoft.Toolkit.Graph.Controls/Providers/NotifyTaskCompletion.cs @@ -0,0 +1,170 @@ +using System; +using System.ComponentModel; +using System.Threading.Tasks; + +namespace Microsoft.Toolkit.Graph.Providers // TODO: Move to base-toolkit +{ + /// + /// Helper class to wrap around a Task to provide more information usable for UI databinding scenarios. As discussed in MSDN Magazine: https://msdn.microsoft.com/magazine/dn605875. + /// + /// Type of result returned by task. + public sealed class NotifyTaskCompletion : INotifyPropertyChanged + { + /// + /// Initializes a new instance of the class. + /// + /// Task to wait on. + public NotifyTaskCompletion(Task task) + { + Task = task; + if (!task.IsCompleted) + { + TaskCompletion = WatchTaskAsync(task); + } + } + + private async Task WatchTaskAsync(Task task) + { + try + { + await task; + } + catch + { + } + + if (PropertyChanged == null) + { + return; + } + + PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(Status))); + PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(IsCompleted))); + PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(IsNotCompleted))); + + if (task.IsCanceled) + { + PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(IsCanceled))); + } + else if (task.IsFaulted) + { + PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(IsFaulted))); + PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(Exception))); + PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(InnerException))); + PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(ErrorMessage))); + } + else + { + PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(IsSuccessfullyCompleted))); + PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(Result))); + } + } + + /// + /// Gets the task that is being waited on. + /// + public Task Task { get; private set; } + + /// + /// Gets the task wrapper task. + /// + public Task TaskCompletion { get; private set; } + + /// + /// Gets the result of the given task. + /// + public TResult Result + { + get + { + return (Task.Status == TaskStatus.RanToCompletion) ? + Task.Result : + default(TResult); + } + } + + /// + /// Gets the status of the task. + /// + public TaskStatus Status + { + get { return Task.Status; } + } + + /// + /// Gets a value indicating whether the task is completed. + /// + public bool IsCompleted + { + get { return Task.IsCompleted; } + } + + /// + /// Gets a value indicating whether the task is not completed. + /// + public bool IsNotCompleted { get { return !Task.IsCompleted; } } + + /// + /// Gets a value indicating whether the task was successfully completed. + /// + public bool IsSuccessfullyCompleted + { + get + { + return Task.Status == TaskStatus.RanToCompletion; + } + } + + /// + /// Gets a value indicating whether the task was cancelled. + /// + public bool IsCanceled + { + get { return Task.IsCanceled; } + } + + /// + /// Gets a value indicating whether there was an error with the task. + /// + public bool IsFaulted + { + get { return Task.IsFaulted; } + } + + /// + /// Gets the exception which occured on the task (if one occurred). + /// + public AggregateException Exception { get { return Task.Exception; } } + + /// + /// Gets the inner exception of the task. + /// + public Exception InnerException + { + get + { + return (Exception == null) ? + null : + Exception.InnerException; + } + } + + /// + /// Gets the error message of the task. + /// + public string ErrorMessage + { + get + { + return (InnerException == null) ? + null : + InnerException.Message; + } + } + + /// + /// PropertyChanged event. + /// + public event PropertyChangedEventHandler PropertyChanged; + } +} \ No newline at end of file diff --git a/Microsoft.Toolkit.Graph/Extensions/GraphExtensions.cs b/Microsoft.Toolkit.Graph/Extensions/GraphExtensions.cs index b56b303..729e48a 100644 --- a/Microsoft.Toolkit.Graph/Extensions/GraphExtensions.cs +++ b/Microsoft.Toolkit.Graph/Extensions/GraphExtensions.cs @@ -2,6 +2,7 @@ // 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.Threading.Tasks; using Microsoft.Graph; @@ -111,5 +112,57 @@ public static T Search(this T request, string query) return request; } + + /// + /// Translate numeric file size to string format. + /// + /// file size in bytes. + /// Returns file size string. + public static string ToFileSizeString(this long size) // TODO: Move this to Microsoft.Toolkit + { + if (size < 1024) + { + return size.ToString("F0") + " bytes"; + } + else if ((size >> 10) < 1024) + { + return (size / (float)1024).ToString("F1") + " KB"; + } + else if ((size >> 20) < 1024) + { + return ((size >> 10) / (float)1024).ToString("F1") + " MB"; + } + else if ((size >> 30) < 1024) + { + return ((size >> 20) / (float)1024).ToString("F1") + " GB"; + } + else if ((size >> 40) < 1024) + { + return ((size >> 30) / (float)1024).ToString("F1") + " TB"; + } + else if ((size >> 50) < 1024) + { + return ((size >> 40) / (float)1024).ToString("F1") + " PB"; + } + else + { + return ((size >> 50) / (float)1024).ToString("F0") + " EB"; + } + } + + /// + /// Extension Helper to help convert timestamp results from the Graph to . + /// + /// Graph Timestamp + /// System Timestamp + public static DateTimeOffset ToDateTimeOffset(this DateTimeTimeZone dttz) + { + // https://docs.microsoft.com/en-us/dotnet/standard/datetime/choosing-between-datetime + var datetime = DateTime.Parse(dttz.DateTime); + var timezone = TimeZoneInfo.FindSystemTimeZoneById(dttz.TimeZone); + var dto = new DateTimeOffset(datetime, timezone.GetUtcOffset(datetime)); + + return dto; + } } } diff --git a/Microsoft.Toolkit.Graph/Providers/ProviderManager.cs b/Microsoft.Toolkit.Graph/Providers/ProviderManager.cs index 39b29f2..ac76b93 100644 --- a/Microsoft.Toolkit.Graph/Providers/ProviderManager.cs +++ b/Microsoft.Toolkit.Graph/Providers/ProviderManager.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.ComponentModel; using Microsoft.Graph; using Microsoft.Identity.Client; using Microsoft.Toolkit.Helpers; @@ -17,7 +18,7 @@ namespace Microsoft.Toolkit.Graph.Providers /// ProviderManager.Instance.GlobalProvider = await MsalProvider.CreateAsync(...); /// /// - public class ProviderManager + public class ProviderManager : INotifyPropertyChanged { /// /// Gets the name of the toolkit client to identify self in Graph calls. @@ -34,6 +35,9 @@ public class ProviderManager /// public event EventHandler ProviderUpdated; + /// + public event PropertyChangedEventHandler PropertyChanged; + private IProvider _provider; /// @@ -61,6 +65,8 @@ public IProvider GlobalProvider } ProviderUpdated?.Invoke(this, new ProviderUpdatedEventArgs(ProviderManagerChangedState.ProviderChanged)); + + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(GlobalProvider))); } } diff --git a/SampleTest/MainPage.xaml b/SampleTest/MainPage.xaml index 8e71b88..045e83d 100644 --- a/SampleTest/MainPage.xaml +++ b/SampleTest/MainPage.xaml @@ -9,8 +9,14 @@ xmlns:graph="using:Microsoft.Graph" xmlns:Interactivity="using:Microsoft.Xaml.Interactivity" xmlns:providers="using:Microsoft.Toolkit.Graph.Providers" + xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls" + xmlns:converters="using:Microsoft.Toolkit.Graph.Controls.Converters" mc:Ignorable="d" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> + + + + + Recent Files: + + + + + + + + + + + + + + + + + + + + + + + + + + + From f6b6a0db9fb1bcc3709cd7261a77ed48dab09809 Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Thu, 25 Jun 2020 11:56:49 -0700 Subject: [PATCH 02/22] Update Example to use ImageEx to show something for files with no images --- SampleTest/Assets/FileIcon.png | Bin 0 -> 2838 bytes SampleTest/MainPage.xaml | 5 +++-- SampleTest/SampleTest.csproj | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 SampleTest/Assets/FileIcon.png diff --git a/SampleTest/Assets/FileIcon.png b/SampleTest/Assets/FileIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..0435822f88e9f95066aa532bc10bbee46fea6a13 GIT binary patch literal 2838 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 literal 0 HcmV?d00001 diff --git a/SampleTest/MainPage.xaml b/SampleTest/MainPage.xaml index c9e1684..00090c8 100644 --- a/SampleTest/MainPage.xaml +++ b/SampleTest/MainPage.xaml @@ -58,8 +58,9 @@ - + diff --git a/SampleTest/SampleTest.csproj b/SampleTest/SampleTest.csproj index e98c1d1..4a1c7d9 100644 --- a/SampleTest/SampleTest.csproj +++ b/SampleTest/SampleTest.csproj @@ -130,6 +130,7 @@ + From eedfc147563ed0bb44cdef2c8fe1974d410df5e6 Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Thu, 25 Jun 2020 13:06:02 -0700 Subject: [PATCH 03/22] Update link to issue filed in Graph for the API we're trying to use --- .../Controls/GraphPresenter/GraphPresenter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/GraphPresenter.cs b/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/GraphPresenter.cs index ad37c23..4133823 100644 --- a/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/GraphPresenter.cs +++ b/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/GraphPresenter.cs @@ -45,7 +45,7 @@ private static async void OnDataChanged(DependencyObject d, DependencyPropertyCh { if (d is GraphPresenter presenter) { - // TODO: Should this be a Graph SDK bug? The interfaces don't match the similar class hierarchy? + // Use BaseRequestBuilder as some interfaces from the Graph SDK don't implement IBaseRequestBuilder properly, see https://github.com/microsoftgraph/msgraph-sdk-dotnet/issues/722 if (e.NewValue is BaseRequestBuilder builder) { var request = new BaseRequest(builder.RequestUrl, builder.Client, null); From 7d43dd841f830e8acc73dc0f7c9b5e03d2d8f3a8 Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Mon, 29 Jun 2020 14:15:47 -0700 Subject: [PATCH 04/22] Update Missing Headers --- .../Controls/GraphPresenter/GraphPresenter.cs | 6 +++++- .../Converters/FileSizeConverter.cs | 6 +++++- .../Converters/OneDriveThumbnailConverter.cs | 6 +++++- .../Providers/NotifyTaskCompletion.cs | 6 +++++- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/GraphPresenter.cs b/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/GraphPresenter.cs index 4133823..762377a 100644 --- a/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/GraphPresenter.cs +++ b/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/GraphPresenter.cs @@ -1,4 +1,8 @@ -using Microsoft.Graph; +// 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 Microsoft.Graph; using Microsoft.Toolkit.Graph.Providers; using Microsoft.Toolkit.Uwp.Helpers; using Newtonsoft.Json; diff --git a/Microsoft.Toolkit.Graph.Controls/Converters/FileSizeConverter.cs b/Microsoft.Toolkit.Graph.Controls/Converters/FileSizeConverter.cs index bff2d95..d02275d 100644 --- a/Microsoft.Toolkit.Graph.Controls/Converters/FileSizeConverter.cs +++ b/Microsoft.Toolkit.Graph.Controls/Converters/FileSizeConverter.cs @@ -1,4 +1,8 @@ -using System; +// 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 Microsoft.Toolkit.Graph.Extensions; using Windows.UI.Xaml.Data; diff --git a/Microsoft.Toolkit.Graph.Controls/Converters/OneDriveThumbnailConverter.cs b/Microsoft.Toolkit.Graph.Controls/Converters/OneDriveThumbnailConverter.cs index 68b3c85..8c2d929 100644 --- a/Microsoft.Toolkit.Graph.Controls/Converters/OneDriveThumbnailConverter.cs +++ b/Microsoft.Toolkit.Graph.Controls/Converters/OneDriveThumbnailConverter.cs @@ -1,4 +1,8 @@ -using System; +// 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 Microsoft.Graph; using Microsoft.Toolkit.Graph.Providers; using Windows.UI.Xaml.Data; diff --git a/Microsoft.Toolkit.Graph.Controls/Providers/NotifyTaskCompletion.cs b/Microsoft.Toolkit.Graph.Controls/Providers/NotifyTaskCompletion.cs index 09091b0..554d76c 100644 --- a/Microsoft.Toolkit.Graph.Controls/Providers/NotifyTaskCompletion.cs +++ b/Microsoft.Toolkit.Graph.Controls/Providers/NotifyTaskCompletion.cs @@ -1,4 +1,8 @@ -using System; +// 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.ComponentModel; using System.Threading.Tasks; From 0c04921a684ae457da138f95f765d1472ed3e999 Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Thu, 16 Jul 2020 12:50:48 -0700 Subject: [PATCH 05/22] [Broken] CalendarView Example Not fully tested as hit error with payload response see https://github.com/microsoftgraph/msgraph-sdk-dotnet/issues/740 --- .../Controls/GraphPresenter/GraphPresenter.cs | 76 ++++++++++++------- .../Controls/GraphPresenter/QueryOption.cs | 30 ++++++++ .../Extensions/GraphExtensions.cs | 27 ++----- SampleTest/MainPage.xaml | 37 +++++---- SampleTest/MainPage.xaml.cs | 12 +++ 5 files changed, 119 insertions(+), 63 deletions(-) create mode 100644 Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/QueryOption.cs diff --git a/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/GraphPresenter.cs b/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/GraphPresenter.cs index 762377a..2b9ca3c 100644 --- a/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/GraphPresenter.cs +++ b/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/GraphPresenter.cs @@ -26,9 +26,9 @@ public class GraphPresenter : ContentPresenter /// /// Gets or sets a to be used to make a request to the graph. The results will be automatically populated to the property. Use a to change the presentation of the data. /// - public object RequestBuilder + public IBaseRequestBuilder RequestBuilder { - get { return (object)GetValue(RequestBuilderProperty); } + get { return (IBaseRequestBuilder)GetValue(RequestBuilderProperty); } set { SetValue(RequestBuilderProperty, value); } } @@ -39,43 +39,65 @@ public object RequestBuilder /// The identifier for the dependency property. /// public static readonly DependencyProperty RequestBuilderProperty = - DependencyProperty.Register(nameof(RequestBuilder), typeof(object), typeof(GraphPresenter), new PropertyMetadata(null, OnDataChanged)); + DependencyProperty.Register(nameof(RequestBuilder), typeof(IBaseRequestBuilder), typeof(GraphPresenter), new PropertyMetadata(null)); + /// + /// Gets or sets the of item returned by the . + /// Set to the base item type and use the property to indicate if a collection is expected back. + /// public Type ResponseType { get; set; } + /// + /// Gets or sets a value indicating whether the returned data from the is a collection. + /// public bool IsCollection { get; set; } - private static async void OnDataChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + /// + /// Gets or sets list of representing values to pass into the request built by . + /// + public List QueryOptions { get; set; } = new List(); + + /// + /// Initializes a new instance of the class. + /// + public GraphPresenter() { - if (d is GraphPresenter presenter) - { - // Use BaseRequestBuilder as some interfaces from the Graph SDK don't implement IBaseRequestBuilder properly, see https://github.com/microsoftgraph/msgraph-sdk-dotnet/issues/722 - if (e.NewValue is BaseRequestBuilder builder) - { - var request = new BaseRequest(builder.RequestUrl, builder.Client, null); - request.Method = "GET"; + Loaded += GraphPresenter_Loaded; + } - var response = await request.SendAsync(null, CancellationToken.None).ConfigureAwait(false) as JObject; + private async void GraphPresenter_Loaded(object sender, RoutedEventArgs e) + { + // Note: some interfaces from the Graph SDK don't implement IBaseRequestBuilder properly, see https://github.com/microsoftgraph/msgraph-sdk-dotnet/issues/722 + if (RequestBuilder != null) + { + var request = new BaseRequest( + RequestBuilder.RequestUrl, + RequestBuilder.Client); // TODO: Do we need separate Options here? + request.Method = "GET"; + request.QueryOptions = QueryOptions?.Select(option => option.ToQueryOption())?.ToList(); - //// TODO: Deal with paging? + // TODO: Add Exception Handling + // Note: CalendarView not supported https://github.com/microsoftgraph/msgraph-sdk-dotnet/issues/740 + var response = await request.SendAsync(null, CancellationToken.None).ConfigureAwait(false) as JObject; - var values = response["value"]; - object data = null; + //// TODO: Deal with paging? - if (presenter.IsCollection) - { - data = values.ToObject(Array.CreateInstance(presenter.ResponseType, 0).GetType()); - } - else - { - data = values.ToObject(presenter.ResponseType); - } + var values = response["value"]; + object data = null; - await DispatcherHelper.ExecuteOnUIThreadAsync(() => - { - presenter.Content = data; - }); + if (IsCollection) + { + data = values.ToObject(Array.CreateInstance(ResponseType, 0).GetType()); } + else + { + data = values.ToObject(ResponseType); + } + + await DispatcherHelper.ExecuteOnUIThreadAsync(() => + { + Content = data; + }); } } } diff --git a/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/QueryOption.cs b/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/QueryOption.cs new file mode 100644 index 0000000..9da4526 --- /dev/null +++ b/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/QueryOption.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Windows.UI.Xaml; + +namespace Microsoft.Toolkit.Graph.Controls +{ + /// + /// XAML Proxy for . + /// + public class QueryOption + { + /// + public string Name { get; set; } + + /// + public string Value { get; set; } + + /// + /// Constructs a value representing this proxy. + /// + /// result. + public Microsoft.Graph.QueryOption ToQueryOption() + { + return new Microsoft.Graph.QueryOption(Name, Value); + } + } +} diff --git a/Microsoft.Toolkit.Graph/Extensions/GraphExtensions.cs b/Microsoft.Toolkit.Graph/Extensions/GraphExtensions.cs index afd9d02..c042189 100644 --- a/Microsoft.Toolkit.Graph/Extensions/GraphExtensions.cs +++ b/Microsoft.Toolkit.Graph/Extensions/GraphExtensions.cs @@ -151,43 +151,28 @@ public static string ToFileSizeString(this long size) // TODO: Move this to Micr } else if ((size >> 10) < 1024) { - return (size / (float)1024).ToString("F1") + " KB"; + return (size / 1024F).ToString("F1") + " KB"; } else if ((size >> 20) < 1024) { - return ((size >> 10) / (float)1024).ToString("F1") + " MB"; + return ((size >> 10) / 1024F).ToString("F1") + " MB"; } else if ((size >> 30) < 1024) { - return ((size >> 20) / (float)1024).ToString("F1") + " GB"; + return ((size >> 20) / 1024F).ToString("F1") + " GB"; } else if ((size >> 40) < 1024) { - return ((size >> 30) / (float)1024).ToString("F1") + " TB"; + return ((size >> 30) / 1024F).ToString("F1") + " TB"; } else if ((size >> 50) < 1024) { - return ((size >> 40) / (float)1024).ToString("F1") + " PB"; + return ((size >> 40) / 1024F).ToString("F1") + " PB"; } else { - return ((size >> 50) / (float)1024).ToString("F0") + " EB"; + return ((size >> 50) / 1024F).ToString("F0") + " EB"; } } - - /// - /// Extension Helper to help convert timestamp results from the Graph to . - /// - /// Graph Timestamp - /// System Timestamp - public static DateTimeOffset ToDateTimeOffset(this DateTimeTimeZone dttz) - { - // https://docs.microsoft.com/en-us/dotnet/standard/datetime/choosing-between-datetime - var datetime = DateTime.Parse(dttz.DateTime); - var timezone = TimeZoneInfo.FindSystemTimeZoneById(dttz.TimeZone); - var dto = new DateTimeOffset(datetime, timezone.GetUtcOffset(datetime)); - - return dto; - } } } diff --git a/SampleTest/MainPage.xaml b/SampleTest/MainPage.xaml index 00090c8..2499c3f 100644 --- a/SampleTest/MainPage.xaml +++ b/SampleTest/MainPage.xaml @@ -2,11 +2,12 @@ x:Class="SampleTest.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:local="using:SampleTest" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:local="using:SampleTest" xmlns:wgt="using:Microsoft.Toolkit.Graph.Controls" xmlns:graph="using:Microsoft.Graph" + xmlns:global="using:System.Globalization" xmlns:Interactivity="using:Microsoft.Xaml.Interactivity" xmlns:providers="using:Microsoft.Toolkit.Graph.Providers" xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls" @@ -47,30 +48,36 @@ Recent Files: - + + + + + - + - + - - - + + + - + + + + + + + + - - - - - diff --git a/SampleTest/MainPage.xaml.cs b/SampleTest/MainPage.xaml.cs index 2689205..60231dc 100644 --- a/SampleTest/MainPage.xaml.cs +++ b/SampleTest/MainPage.xaml.cs @@ -2,6 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.Graph; +using Microsoft.Graph.Extensions; +using System; using Windows.UI.Xaml.Controls; namespace SampleTest @@ -11,9 +14,18 @@ namespace SampleTest /// public sealed partial class MainPage : Page { + public DateTime Today => DateTimeOffset.Now.Date.ToUniversalTime(); + + public DateTime ThreeDaysFromNow => Today.AddDays(3); + public MainPage() { this.InitializeComponent(); } + + public static string ToLocalTime(DateTimeTimeZone value) + { + return value.ToDateTimeOffset().LocalDateTime.ToString("g"); + } } } From e5b3013d7787fa78c55e106576379bc7902cce2a Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Thu, 16 Jul 2020 12:52:16 -0700 Subject: [PATCH 06/22] Add forgotten .NET Foundation Header --- .../Controls/GraphPresenter/QueryOption.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/QueryOption.cs b/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/QueryOption.cs index 9da4526..7cce86a 100644 --- a/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/QueryOption.cs +++ b/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/QueryOption.cs @@ -1,4 +1,8 @@ -using System; +// 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; From 2a9e157a619d88e054f88253b0d0a95e73170683 Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Thu, 16 Jul 2020 13:05:40 -0700 Subject: [PATCH 07/22] Add OrderBy property to GraphPresenter --- .../Controls/GraphPresenter/GraphPresenter.cs | 26 ++++++++++++++----- SampleTest/MainPage.xaml | 1 + 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/GraphPresenter.cs b/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/GraphPresenter.cs index 2b9ca3c..de0c006 100644 --- a/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/GraphPresenter.cs +++ b/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/GraphPresenter.cs @@ -2,17 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Microsoft.Graph; -using Microsoft.Toolkit.Graph.Providers; -using Microsoft.Toolkit.Uwp.Helpers; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Linq; -using System.Text; using System.Threading; -using System.Threading.Tasks; +using Microsoft.Graph; +using Microsoft.Toolkit.Uwp.Helpers; +using Newtonsoft.Json.Linq; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; @@ -57,6 +53,11 @@ public IBaseRequestBuilder RequestBuilder /// public List QueryOptions { get; set; } = new List(); + /// + /// Gets or sets a string to indicate a sorting order for the . This is a helper to add this specific request option to the . + /// + public string OrderBy { get; set; } + /// /// Initializes a new instance of the class. /// @@ -76,6 +77,17 @@ private async void GraphPresenter_Loaded(object sender, RoutedEventArgs e) request.Method = "GET"; request.QueryOptions = QueryOptions?.Select(option => option.ToQueryOption())?.ToList(); + if (request.QueryOptions == null) + { + request.QueryOptions = new List(); + } + + // Handle Special QueryOptions + if (!string.IsNullOrWhiteSpace(OrderBy)) + { + request.QueryOptions.Add(new Microsoft.Graph.QueryOption("$orderby", OrderBy)); + } + // TODO: Add Exception Handling // Note: CalendarView not supported https://github.com/microsoftgraph/msgraph-sdk-dotnet/issues/740 var response = await request.SendAsync(null, CancellationToken.None).ConfigureAwait(false) as JObject; diff --git a/SampleTest/MainPage.xaml b/SampleTest/MainPage.xaml index 2499c3f..da167aa 100644 --- a/SampleTest/MainPage.xaml +++ b/SampleTest/MainPage.xaml @@ -49,6 +49,7 @@ Recent Files: From 6556c2d7d337a5abe4b25864306cb87ce65d0e8e Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Thu, 23 Jul 2020 15:43:19 -0700 Subject: [PATCH 08/22] Try updating dependencies --- .../Microsoft.Toolkit.Graph.Controls.csproj | 4 ++-- Microsoft.Toolkit.Graph/Microsoft.Toolkit.Graph.csproj | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Microsoft.Toolkit.Graph.Controls/Microsoft.Toolkit.Graph.Controls.csproj b/Microsoft.Toolkit.Graph.Controls/Microsoft.Toolkit.Graph.Controls.csproj index 6cd7fde..5dbc42e 100644 --- a/Microsoft.Toolkit.Graph.Controls/Microsoft.Toolkit.Graph.Controls.csproj +++ b/Microsoft.Toolkit.Graph.Controls/Microsoft.Toolkit.Graph.Controls.csproj @@ -34,8 +34,8 @@ - - + + diff --git a/Microsoft.Toolkit.Graph/Microsoft.Toolkit.Graph.csproj b/Microsoft.Toolkit.Graph/Microsoft.Toolkit.Graph.csproj index 8e49be7..5164e15 100644 --- a/Microsoft.Toolkit.Graph/Microsoft.Toolkit.Graph.csproj +++ b/Microsoft.Toolkit.Graph/Microsoft.Toolkit.Graph.csproj @@ -21,8 +21,8 @@ - - + + From ff9ee756fce2d870415cd344f914c980ab6cd476 Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Sun, 26 Jul 2020 16:16:25 -0700 Subject: [PATCH 09/22] Fix Stylecop Issues --- .../Converters/FileSizeConverter.cs | 5 +++++ .../Providers/NotifyTaskCompletion.cs | 12 +++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Microsoft.Toolkit.Graph.Controls/Converters/FileSizeConverter.cs b/Microsoft.Toolkit.Graph.Controls/Converters/FileSizeConverter.cs index d02275d..0a86246 100644 --- a/Microsoft.Toolkit.Graph.Controls/Converters/FileSizeConverter.cs +++ b/Microsoft.Toolkit.Graph.Controls/Converters/FileSizeConverter.cs @@ -8,8 +8,12 @@ namespace Microsoft.Toolkit.Graph.Controls.Converters { + /// + /// The takes a long value in and converts it to a human readible string using the method. + /// public class FileSizeConverter : IValueConverter { + /// public object Convert(object value, Type targetType, object parameter, string language) { if (value is long size) @@ -20,6 +24,7 @@ public object Convert(object value, Type targetType, object parameter, string la return null; } + /// public object ConvertBack(object value, Type targetType, object parameter, string language) { throw new NotImplementedException(); diff --git a/Microsoft.Toolkit.Graph.Controls/Providers/NotifyTaskCompletion.cs b/Microsoft.Toolkit.Graph.Controls/Providers/NotifyTaskCompletion.cs index 554d76c..a3422fb 100644 --- a/Microsoft.Toolkit.Graph.Controls/Providers/NotifyTaskCompletion.cs +++ b/Microsoft.Toolkit.Graph.Controls/Providers/NotifyTaskCompletion.cs @@ -6,7 +6,7 @@ using System.ComponentModel; using System.Threading.Tasks; -namespace Microsoft.Toolkit.Graph.Providers // TODO: Move to base-toolkit +namespace Microsoft.Toolkit.Graph.Providers // TODO: Remove { /// /// Helper class to wrap around a Task to provide more information usable for UI databinding scenarios. As discussed in MSDN Magazine: https://msdn.microsoft.com/magazine/dn605875. @@ -106,7 +106,10 @@ public bool IsCompleted /// /// Gets a value indicating whether the task is not completed. /// - public bool IsNotCompleted { get { return !Task.IsCompleted; } } + public bool IsNotCompleted + { + get { return !Task.IsCompleted; } + } /// /// Gets a value indicating whether the task was successfully completed. @@ -138,7 +141,10 @@ public bool IsFaulted /// /// Gets the exception which occured on the task (if one occurred). /// - public AggregateException Exception { get { return Task.Exception; } } + public AggregateException Exception + { + get { return Task.Exception; } + } /// /// Gets the inner exception of the task. From dc8a6417d6f68d0b7f8a9ab5d3095077946d229b Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Sun, 26 Jul 2020 17:36:07 -0700 Subject: [PATCH 10/22] Reconfigure Proxy to work in all cases and more effectively Update Calendar Example Test --- .../Providers/MockProvider.cs | 27 ++++++++----------- SampleTest/MainPage.xaml | 4 +-- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/Microsoft.Toolkit.Graph/Providers/MockProvider.cs b/Microsoft.Toolkit.Graph/Providers/MockProvider.cs index 863c3dd..d0f40d2 100644 --- a/Microsoft.Toolkit.Graph/Providers/MockProvider.cs +++ b/Microsoft.Toolkit.Graph/Providers/MockProvider.cs @@ -21,6 +21,8 @@ namespace Microsoft.Toolkit.Graph.Providers /// public class MockProvider : IProvider { + private static readonly string GRAPH_PROXY_URL = "https://proxy.apisandbox.msdn.microsoft.com/svc?url="; + private ProviderState _state = ProviderState.Loading; /// @@ -42,22 +44,15 @@ private set /// public GraphServiceClient Graph => new GraphServiceClient( - "https://proxy.apisandbox.msdn.microsoft.com/svc?url=" + HttpUtility.HtmlEncode("https://graph.microsoft.com/beta/"), - new DelegateAuthenticationProvider((requestMessage) => - { - //// Temporary Workaround for https://github.com/microsoftgraph/msgraph-sdk-dotnet-core/issues/59 - //// ------------------------ - var requestUri = requestMessage.RequestUri.ToString(); - var index = requestUri.IndexOf("&"); - if (index >= 0) - { - requestMessage.RequestUri = new Uri(requestUri.Remove(index, 1).Insert(index, "?")); - } - - //// End Workaround - - return this.AuthenticateRequestAsync(requestMessage); - })); + new DelegateAuthenticationProvider((requestMessage) => + { + var requestUri = requestMessage.RequestUri.ToString(); + + // Prepend Proxy Service URI to our request + requestMessage.RequestUri = new Uri(GRAPH_PROXY_URL + HttpUtility.UrlEncode(requestUri)); + + return this.AuthenticateRequestAsync(requestMessage); + })); /// public event EventHandler StateChanged; diff --git a/SampleTest/MainPage.xaml b/SampleTest/MainPage.xaml index da167aa..7876f20 100644 --- a/SampleTest/MainPage.xaml +++ b/SampleTest/MainPage.xaml @@ -65,14 +65,14 @@ + - - - + From b70e6b70c11aafdf209fe58406cf0686fd0c2f6b Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Sun, 26 Jul 2020 18:18:31 -0700 Subject: [PATCH 11/22] Better separate out Samples in UWP app --- SampleTest/MainPage.xaml | 112 +++++++++++++++++++++++++++------------ 1 file changed, 78 insertions(+), 34 deletions(-) diff --git a/SampleTest/MainPage.xaml b/SampleTest/MainPage.xaml index 7876f20..01f7976 100644 --- a/SampleTest/MainPage.xaml +++ b/SampleTest/MainPage.xaml @@ -32,10 +32,25 @@ - - - + + + + + + + + The `LoginButton` above allows your user and application to easily connect to the Microsoft Graph. They can then also easily logout from the drop-down menu. + + + + + + + The `PeoplePicker` lets a logged in user easily search for familiar people they interact with or contacts. Great for emails or messages. + Picked People: - - Recent Files: - + + + + + + + + + The `GraphPresenter` is a unique control in the library which makes it easier for a developer to make any graph call and configure a nice display template in XAML. This opens up a world of possibilities for many uses outside of any other control available within this library. You can see a few examples of what's possible below. + + + + + + + + + The following example shows the `Me.CalendarView` API. + My Upcoming Calendar Events: + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - - + + - - + + + + + + + + + - - - - - - - - - + + + + + + + + + From f028d3fe4956c73a81ba2649b9c3478685892a05 Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Sun, 26 Jul 2020 19:00:03 -0700 Subject: [PATCH 12/22] Add (Mail) Messages GraphPresenter sample --- SampleTest/MainPage.xaml | 34 ++++++++++++++++++++++++++++++++++ SampleTest/MainPage.xaml.cs | 7 +++++++ 2 files changed, 41 insertions(+) diff --git a/SampleTest/MainPage.xaml b/SampleTest/MainPage.xaml index 01f7976..8ee7c8e 100644 --- a/SampleTest/MainPage.xaml +++ b/SampleTest/MainPage.xaml @@ -127,6 +127,40 @@ + + + + The following example shows the `Me.Messages` API for getting a user's inbox mail messages. + My Messages: + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SampleTest/MainPage.xaml.cs b/SampleTest/MainPage.xaml.cs index 60231dc..9b524f6 100644 --- a/SampleTest/MainPage.xaml.cs +++ b/SampleTest/MainPage.xaml.cs @@ -5,6 +5,7 @@ using Microsoft.Graph; using Microsoft.Graph.Extensions; using System; +using System.Text.RegularExpressions; using Windows.UI.Xaml.Controls; namespace SampleTest @@ -27,5 +28,11 @@ public static string ToLocalTime(DateTimeTimeZone value) { return value.ToDateTimeOffset().LocalDateTime.ToString("g"); } + + public static string RemoveWhitespace(string value) + { + //// Workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/2654 + return Regex.Replace(value, @"\t|\r|\n", " "); + } } } From a4919fb3d2f3bbe949b87b895905b4b90db9333e Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Thu, 6 Aug 2020 11:13:51 -0700 Subject: [PATCH 13/22] Add Task sample to GraphPresenter --- SampleTest/MainPage.xaml | 62 +++++++++++++++++++++++++++++++++++++ SampleTest/MainPage.xaml.cs | 10 ++++++ 2 files changed, 72 insertions(+) diff --git a/SampleTest/MainPage.xaml b/SampleTest/MainPage.xaml index 8ee7c8e..c6ca60e 100644 --- a/SampleTest/MainPage.xaml +++ b/SampleTest/MainPage.xaml @@ -161,6 +161,68 @@ + + + + The following example shows the `Me.Planner.Tasks` API for getting a user's tasks. + My Tasks: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Due + + + + + + + + + + + + + + + + + diff --git a/SampleTest/MainPage.xaml.cs b/SampleTest/MainPage.xaml.cs index 9b524f6..38fe405 100644 --- a/SampleTest/MainPage.xaml.cs +++ b/SampleTest/MainPage.xaml.cs @@ -29,10 +29,20 @@ public static string ToLocalTime(DateTimeTimeZone value) return value.ToDateTimeOffset().LocalDateTime.ToString("g"); } + public static string ToLocalTime(DateTimeOffset? value) + { + return value?.LocalDateTime.ToString("g"); + } + public static string RemoveWhitespace(string value) { //// Workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/2654 return Regex.Replace(value, @"\t|\r|\n", " "); } + + public static bool IsTaskCompleted(int? percentCompleted) + { + return percentCompleted == 100; + } } } From 7cdd34f786cf3b81ec0be7d00824f4179f8231a4 Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Thu, 6 Aug 2020 12:30:23 -0700 Subject: [PATCH 14/22] Add in Teams Messages example to GraphPresenter TODO: Display HTML better or strip out tags. --- SampleTest/MainPage.xaml | 37 +++++++++++++++++++++++++++++++++++++ SampleTest/MainPage.xaml.cs | 11 +++++++++++ 2 files changed, 48 insertions(+) diff --git a/SampleTest/MainPage.xaml b/SampleTest/MainPage.xaml index c6ca60e..8e4845c 100644 --- a/SampleTest/MainPage.xaml +++ b/SampleTest/MainPage.xaml @@ -223,6 +223,43 @@ + + + + The following example shows the beta `Teams/id/Channels/id/messages` API for getting a list of messages (without replies) from a Channel in Teams. + My Chat Messages: + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SampleTest/MainPage.xaml.cs b/SampleTest/MainPage.xaml.cs index 38fe405..174e225 100644 --- a/SampleTest/MainPage.xaml.cs +++ b/SampleTest/MainPage.xaml.cs @@ -4,6 +4,7 @@ using Microsoft.Graph; using Microsoft.Graph.Extensions; +using Microsoft.Toolkit.Graph.Providers; using System; using System.Text.RegularExpressions; using Windows.UI.Xaml.Controls; @@ -15,8 +16,10 @@ namespace SampleTest /// public sealed partial class MainPage : Page { + //// Workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/2407 public DateTime Today => DateTimeOffset.Now.Date.ToUniversalTime(); + //// Workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/2407 public DateTime ThreeDaysFromNow => Today.AddDays(3); public MainPage() @@ -26,11 +29,13 @@ public MainPage() public static string ToLocalTime(DateTimeTimeZone value) { + //// Workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/2407 return value.ToDateTimeOffset().LocalDateTime.ToString("g"); } public static string ToLocalTime(DateTimeOffset? value) { + //// Workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/2654 return value?.LocalDateTime.ToString("g"); } @@ -44,5 +49,11 @@ public static bool IsTaskCompleted(int? percentCompleted) { return percentCompleted == 100; } + + public static IBaseRequestBuilder GetTeamsChannelMessagesBuilder(string team, string channel) + { + //// Workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/3064 + return ProviderManager.Instance.GlobalProvider.Graph.Teams[team].Channels[channel].Messages; + } } } From b93e18c5119e5ca5b45ef5d950a5ba07ea8c997a Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Fri, 7 Aug 2020 17:32:45 -0700 Subject: [PATCH 15/22] Remove unused OneDrive sample Converters (they need re-work see #55) Also updated Teams sample messages to remove HTML tags for now. --- .../Converters/FileSizeConverter.cs | 33 ---- .../Converters/OneDriveThumbnailConverter.cs | 39 ---- .../Providers/NotifyTaskCompletion.cs | 180 ------------------ SampleTest/MainPage.xaml | 8 +- 4 files changed, 2 insertions(+), 258 deletions(-) delete mode 100644 Microsoft.Toolkit.Graph.Controls/Converters/FileSizeConverter.cs delete mode 100644 Microsoft.Toolkit.Graph.Controls/Converters/OneDriveThumbnailConverter.cs delete mode 100644 Microsoft.Toolkit.Graph.Controls/Providers/NotifyTaskCompletion.cs diff --git a/Microsoft.Toolkit.Graph.Controls/Converters/FileSizeConverter.cs b/Microsoft.Toolkit.Graph.Controls/Converters/FileSizeConverter.cs deleted file mode 100644 index 0a86246..0000000 --- a/Microsoft.Toolkit.Graph.Controls/Converters/FileSizeConverter.cs +++ /dev/null @@ -1,33 +0,0 @@ -// 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 Microsoft.Toolkit.Graph.Extensions; -using Windows.UI.Xaml.Data; - -namespace Microsoft.Toolkit.Graph.Controls.Converters -{ - /// - /// The takes a long value in and converts it to a human readible string using the method. - /// - public class FileSizeConverter : IValueConverter - { - /// - public object Convert(object value, Type targetType, object parameter, string language) - { - if (value is long size) - { - return size.ToFileSizeString(); - } - - return null; - } - - /// - public object ConvertBack(object value, Type targetType, object parameter, string language) - { - throw new NotImplementedException(); - } - } -} diff --git a/Microsoft.Toolkit.Graph.Controls/Converters/OneDriveThumbnailConverter.cs b/Microsoft.Toolkit.Graph.Controls/Converters/OneDriveThumbnailConverter.cs deleted file mode 100644 index 8c2d929..0000000 --- a/Microsoft.Toolkit.Graph.Controls/Converters/OneDriveThumbnailConverter.cs +++ /dev/null @@ -1,39 +0,0 @@ -// 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 Microsoft.Graph; -using Microsoft.Toolkit.Graph.Providers; -using Windows.UI.Xaml.Data; - -namespace Microsoft.Toolkit.Graph.Controls.Converters -{ - /// - /// Helper to return a . - /// - public class OneDriveThumbnailConverter : IValueConverter - { - /// - public object Convert(object value, Type targetType, object parameter, string language) - { - if (value is RemoteItem ri) - { - // drives/${file.remoteItem.parentReference.driveId}/items/${file.remoteItem.id}/thumbnails/0/medium - var provider = ProviderManager.Instance.GlobalProvider; - if (provider != null && provider.Graph != null) - { - return new NotifyTaskCompletion(provider.Graph.Drives[ri.ParentReference.DriveId].Items[ri.Id].Thumbnails["0"].Request().GetAsync()); - } - } - - return null; - } - - /// - public object ConvertBack(object value, Type targetType, object parameter, string language) - { - throw new NotImplementedException(); - } - } -} diff --git a/Microsoft.Toolkit.Graph.Controls/Providers/NotifyTaskCompletion.cs b/Microsoft.Toolkit.Graph.Controls/Providers/NotifyTaskCompletion.cs deleted file mode 100644 index a3422fb..0000000 --- a/Microsoft.Toolkit.Graph.Controls/Providers/NotifyTaskCompletion.cs +++ /dev/null @@ -1,180 +0,0 @@ -// 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.ComponentModel; -using System.Threading.Tasks; - -namespace Microsoft.Toolkit.Graph.Providers // TODO: Remove -{ - /// - /// Helper class to wrap around a Task to provide more information usable for UI databinding scenarios. As discussed in MSDN Magazine: https://msdn.microsoft.com/magazine/dn605875. - /// - /// Type of result returned by task. - public sealed class NotifyTaskCompletion : INotifyPropertyChanged - { - /// - /// Initializes a new instance of the class. - /// - /// Task to wait on. - public NotifyTaskCompletion(Task task) - { - Task = task; - if (!task.IsCompleted) - { - TaskCompletion = WatchTaskAsync(task); - } - } - - private async Task WatchTaskAsync(Task task) - { - try - { - await task; - } - catch - { - } - - if (PropertyChanged == null) - { - return; - } - - PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(Status))); - PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(IsCompleted))); - PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(IsNotCompleted))); - - if (task.IsCanceled) - { - PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(IsCanceled))); - } - else if (task.IsFaulted) - { - PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(IsFaulted))); - PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(Exception))); - PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(InnerException))); - PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(ErrorMessage))); - } - else - { - PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(IsSuccessfullyCompleted))); - PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(Result))); - } - } - - /// - /// Gets the task that is being waited on. - /// - public Task Task { get; private set; } - - /// - /// Gets the task wrapper task. - /// - public Task TaskCompletion { get; private set; } - - /// - /// Gets the result of the given task. - /// - public TResult Result - { - get - { - return (Task.Status == TaskStatus.RanToCompletion) ? - Task.Result : - default(TResult); - } - } - - /// - /// Gets the status of the task. - /// - public TaskStatus Status - { - get { return Task.Status; } - } - - /// - /// Gets a value indicating whether the task is completed. - /// - public bool IsCompleted - { - get { return Task.IsCompleted; } - } - - /// - /// Gets a value indicating whether the task is not completed. - /// - public bool IsNotCompleted - { - get { return !Task.IsCompleted; } - } - - /// - /// Gets a value indicating whether the task was successfully completed. - /// - public bool IsSuccessfullyCompleted - { - get - { - return Task.Status == TaskStatus.RanToCompletion; - } - } - - /// - /// Gets a value indicating whether the task was cancelled. - /// - public bool IsCanceled - { - get { return Task.IsCanceled; } - } - - /// - /// Gets a value indicating whether there was an error with the task. - /// - public bool IsFaulted - { - get { return Task.IsFaulted; } - } - - /// - /// Gets the exception which occured on the task (if one occurred). - /// - public AggregateException Exception - { - get { return Task.Exception; } - } - - /// - /// Gets the inner exception of the task. - /// - public Exception InnerException - { - get - { - return (Exception == null) ? - null : - Exception.InnerException; - } - } - - /// - /// Gets the error message of the task. - /// - public string ErrorMessage - { - get - { - return (InnerException == null) ? - null : - InnerException.Message; - } - } - - /// - /// PropertyChanged event. - /// - public event PropertyChangedEventHandler PropertyChanged; - } -} \ No newline at end of file diff --git a/SampleTest/MainPage.xaml b/SampleTest/MainPage.xaml index 8e4845c..ad16332 100644 --- a/SampleTest/MainPage.xaml +++ b/SampleTest/MainPage.xaml @@ -11,14 +11,10 @@ xmlns:Interactivity="using:Microsoft.Xaml.Interactivity" xmlns:providers="using:Microsoft.Toolkit.Graph.Providers" xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls" - xmlns:converters="using:Microsoft.Toolkit.Graph.Controls.Converters" + xmlns:ex="using:Microsoft.Toolkit.Extensions" mc:Ignorable="d" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" xmlns:controlsKeepThis="using:Microsoft.Toolkit.Uwp.UI.Controls"> - - - - - + From 3cc9ec38e35a9bceef1d2c8d72fec991bf46368c Mon Sep 17 00:00:00 2001 From: "Michael Hawker MSFT (XAML Llama)" <24302614+michael-hawker@users.noreply.github.com> Date: Mon, 10 Aug 2020 18:35:14 -0700 Subject: [PATCH 20/22] Update Microsoft.Toolkit.Graph/Providers/MockProvider.cs --- Microsoft.Toolkit.Graph/Providers/MockProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Graph/Providers/MockProvider.cs b/Microsoft.Toolkit.Graph/Providers/MockProvider.cs index 9e65fbc..a0d54ec 100644 --- a/Microsoft.Toolkit.Graph/Providers/MockProvider.cs +++ b/Microsoft.Toolkit.Graph/Providers/MockProvider.cs @@ -49,7 +49,7 @@ private set var requestUri = requestMessage.RequestUri.ToString(); // Prepend Proxy Service URI to our request - requestMessage.RequestUri = new Uri(GRAPH_PROXY_URL + HttpUtility.UrlEncode(requestUri)); + requestMessage.RequestUri = new Uri(GRAPH_PROXY_URL + Uri.EscapeDataString(requestUri)); return this.AuthenticateRequestAsync(requestMessage); })); From f012a4e0bf71fb61e398a3c94b1ddb89fe52d17b Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Tue, 11 Aug 2020 11:04:50 -0700 Subject: [PATCH 21/22] Address PR comments --- .../Controls/GraphPresenter/GraphPresenter.cs | 2 +- .../Controls/GraphPresenter/QueryOption.cs | 8 ++++---- SampleTest/MainPage.xaml.cs | 12 ++++++------ 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/GraphPresenter.cs b/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/GraphPresenter.cs index 930d27d..5fa1a3c 100644 --- a/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/GraphPresenter.cs +++ b/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/GraphPresenter.cs @@ -75,7 +75,7 @@ private async void GraphPresenter_Loaded(object sender, RoutedEventArgs e) RequestBuilder.RequestUrl, RequestBuilder.Client); // TODO: Do we need separate Options here? request.Method = "GET"; - request.QueryOptions = QueryOptions?.Select(option => option.ToQueryOption())?.ToList() ?? new List(); + request.QueryOptions = QueryOptions?.Select(option => (Microsoft.Graph.QueryOption)option)?.ToList() ?? new List(); // Handle Special QueryOptions if (!string.IsNullOrWhiteSpace(OrderBy)) diff --git a/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/QueryOption.cs b/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/QueryOption.cs index ff05eb3..bce6e8f 100644 --- a/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/QueryOption.cs +++ b/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/QueryOption.cs @@ -23,12 +23,12 @@ public sealed class QueryOption public string Value { get; set; } /// - /// Constructs a value representing this proxy. + /// Implicit conversion for to . /// - /// result. - public Microsoft.Graph.QueryOption ToQueryOption() + /// query option to convert + public static implicit operator Microsoft.Graph.QueryOption(QueryOption option) { - return new Microsoft.Graph.QueryOption(Name, Value); + return new Microsoft.Graph.QueryOption(option.Name, option.Value); } } } diff --git a/SampleTest/MainPage.xaml.cs b/SampleTest/MainPage.xaml.cs index 174e225..428741f 100644 --- a/SampleTest/MainPage.xaml.cs +++ b/SampleTest/MainPage.xaml.cs @@ -16,10 +16,10 @@ namespace SampleTest /// public sealed partial class MainPage : Page { - //// Workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/2407 + // Workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/2407 public DateTime Today => DateTimeOffset.Now.Date.ToUniversalTime(); - //// Workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/2407 + // Workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/2407 public DateTime ThreeDaysFromNow => Today.AddDays(3); public MainPage() @@ -29,19 +29,19 @@ public MainPage() public static string ToLocalTime(DateTimeTimeZone value) { - //// Workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/2407 + // Workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/2407 return value.ToDateTimeOffset().LocalDateTime.ToString("g"); } public static string ToLocalTime(DateTimeOffset? value) { - //// Workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/2654 + // Workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/2654 return value?.LocalDateTime.ToString("g"); } public static string RemoveWhitespace(string value) { - //// Workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/2654 + // Workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/2654 return Regex.Replace(value, @"\t|\r|\n", " "); } @@ -52,7 +52,7 @@ public static bool IsTaskCompleted(int? percentCompleted) public static IBaseRequestBuilder GetTeamsChannelMessagesBuilder(string team, string channel) { - //// Workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/3064 + // Workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/3064 return ProviderManager.Instance.GlobalProvider.Graph.Teams[team].Channels[channel].Messages; } } From c2dd9ea179c2b8bffaa7cdf5158ad6d9501b46e0 Mon Sep 17 00:00:00 2001 From: Alexandre Zollinger Chohfi Date: Tue, 11 Aug 2020 12:01:19 -0700 Subject: [PATCH 22/22] Update Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/QueryOption.cs --- .../Controls/GraphPresenter/QueryOption.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/QueryOption.cs b/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/QueryOption.cs index bce6e8f..8af7159 100644 --- a/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/QueryOption.cs +++ b/Microsoft.Toolkit.Graph.Controls/Controls/GraphPresenter/QueryOption.cs @@ -25,7 +25,7 @@ public sealed class QueryOption /// /// Implicit conversion for to . /// - /// query option to convert + /// query option to convert. public static implicit operator Microsoft.Graph.QueryOption(QueryOption option) { return new Microsoft.Graph.QueryOption(option.Name, option.Value);