Skip to content
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
8f63e24
wip
shweaver-MSFT Feb 22, 2021
a425f1c
Added custom roaming settings
shweaver-MSFT Feb 24, 2021
838bff7
Moved some things around
shweaver-MSFT Feb 24, 2021
5e2d469
Added indexer to UserExtensionDataStore
shweaver-MSFT Feb 25, 2021
d290ffe
Much debugging
shweaver-MSFT Feb 25, 2021
728a30f
UX improvements
shweaver-MSFT Feb 25, 2021
c60d10c
Removing clientId from sample app
shweaver-MSFT Feb 25, 2021
51a3c03
Reverting changes in MainPage.xaml.cs
shweaver-MSFT Feb 25, 2021
2224716
Fixed build warnings
shweaver-MSFT Mar 1, 2021
9f3829c
wip
shweaver-MSFT Mar 9, 2021
36158cb
wip
shweaver-MSFT Mar 10, 2021
482fc69
Typo
shweaver-MSFT Mar 10, 2021
e885706
Simplified creation of RoamingSettings helper. Moved files to Control…
shweaver-MSFT Mar 12, 2021
a44ef0d
Removed accidental uwp reference in base package
shweaver-MSFT Mar 12, 2021
431716d
Accidental whitespace
shweaver-MSFT Mar 12, 2021
d61ffa5
Added static helper method for easily creating RoamingSettingsHelper …
shweaver-MSFT Mar 15, 2021
2713dd3
Updates from hackathon
shweaver-MSFT Mar 18, 2021
421b65c
Manual merge
shweaver-MSFT Mar 24, 2021
d1194eb
Fix for bug in primitive deserialization
shweaver-MSFT Mar 24, 2021
092b779
Removed client id
shweaver-MSFT Mar 24, 2021
c6eb117
Merge branch 'dev' into shweaver/roaming-settings
shweaver-MSFT Mar 24, 2021
e041661
Manual merge
shweaver-MSFT Mar 24, 2021
d1b4436
Revert "Manual merge"
shweaver-MSFT Mar 25, 2021
1a35447
Minor fixes
shweaver-MSFT Mar 25, 2021
538c6af
Minor fixes
shweaver-MSFT Mar 25, 2021
d00e965
Changed autosync behavior and added syncOnInit flag in RoamingSetting…
shweaver-MSFT Mar 26, 2021
4b9a5f3
Added list of known reserved keys
shweaver-MSFT Mar 26, 2021
b1d1b01
Bug fixes
shweaver-MSFT Mar 27, 2021
17e46d4
Nitpick
shweaver-MSFT Mar 27, 2021
43be9f1
Autosync fixes and caching adjustments
shweaver-MSFT Mar 27, 2021
da57b77
Fix to CreateForUser static method
shweaver-MSFT Mar 27, 2021
a5d6737
Merge branch 'dev' into shweaver/roaming-settings
shweaver-MSFT Mar 27, 2021
b2d16bd
Adjusted argument order in roamingSettings via user extensions
shweaver-MSFT Mar 29, 2021
8dea981
Merge branch 'dev' into shweaver/roaming-settings
shweaver-MSFT Mar 29, 2021
36a21b5
Replacing JsonObjectSerializer with SystemSerializer
shweaver-MSFT Mar 29, 2021
3bd8b5c
Added SyncCompleted and Failed events with unit test for UserExtensio…
shweaver-MSFT Mar 31, 2021
4da9e28
Allowing RoamingSettingsHelper to save new items via indexer
shweaver-MSFT Apr 2, 2021
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
16 changes: 16 additions & 0 deletions CommunityToolkit.Uwp.Graph.Controls/Common/JsonObjectSerializer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// 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.Text.Json;
using Microsoft.Toolkit.Uwp.Helpers;

namespace CommunityToolkit.Uwp.Graph.Common
{
internal class JsonObjectSerializer : IObjectSerializer
{
public string Serialize<T>(T value) => JsonSerializer.Serialize(value);

public T Deserialize<T>(string value) => JsonSerializer.Deserialize<T>(value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// 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.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Toolkit.Uwp.Helpers;

namespace CommunityToolkit.Uwp.Graph.Helpers.RoamingSettings
{
/// <summary>
/// Defines the contract for creating storage containers used for roaming data.
/// </summary>
public interface IRoamingSettingsDataStore : IObjectStorageHelper
{
/// <summary>
/// Gets access to the key/value pairs cache directly.
/// </summary>
IDictionary<string, object> Settings { get; }

/// <summary>
/// Create a new storage container.
/// </summary>
/// <returns>A Task.</returns>
Task Create();

/// <summary>
/// Delete the existing storage container.
/// </summary>
/// <returns>A Task.</returns>
Task Delete();

/// <summary>
/// Syncronize the internal cache with the remote storage endpoint.
/// </summary>
/// <returns>A Task.</returns>
Task Sync();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
// 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.Threading.Tasks;
using CommunityToolkit.Net.Authentication;
using CommunityToolkit.Net.Graph.Extensions;
using CommunityToolkit.Uwp.Graph.Common;
using Microsoft.Toolkit.Uwp.Helpers;
using Windows.Storage;

namespace CommunityToolkit.Uwp.Graph.Helpers.RoamingSettings
{
/// <summary>
/// An enumeration of the available data storage methods for roaming data.
/// </summary>
public enum RoamingDataStore
{
/// <summary>
/// Store data using open extensions on the Graph User.
/// </summary>
UserExtensions,
}

/// <summary>
/// A helper class for syncing data to roaming data store.
/// </summary>
public class RoamingSettingsHelper : IRoamingSettingsDataStore
{
/// <summary>
/// Gets the internal data storage helper instance.
/// </summary>
public IRoamingSettingsDataStore DataStore { get; private set; }

/// <inheritdoc />
public IDictionary<string, object> Settings => DataStore?.Settings;

/// <summary>
/// Creates a new RoamingSettingsHelper instance for the currently signed in user.
/// </summary>
/// <param name="dataStore">Which specific data store is being used.</param>
/// <param name="autoSync">Whether the values should immediately sync or not.</param>
/// <param name="serializer">An object serializer for serialization of objects in the data store.</param>
/// <returns>A new instance of the RoamingSettingsHelper configured for the current user.</returns>
public static async Task<RoamingSettingsHelper> CreateForCurrentUser(RoamingDataStore dataStore = RoamingDataStore.UserExtensions, bool autoSync = true, IObjectSerializer serializer = null)
{
var provider = ProviderManager.Instance.GlobalProvider;
if (provider == null || provider.State != ProviderState.SignedIn)
{
throw new InvalidOperationException("The GlobalProvider must be set and signed in to create a new RoamingSettingsHelper for the current user.");
}

var me = await provider.Graph().Me.Request().GetAsync();
return new RoamingSettingsHelper(me.Id, dataStore, autoSync, serializer);
}

/// <summary>
/// Initializes a new instance of the <see cref="RoamingSettingsHelper"/> class.
/// </summary>
/// <param name="userId">The id of the target Graph User.</param>
/// <param name="dataStore">Which specific data store is being used.</param>
/// <param name="autoSync">Whether the values should immediately sync or not.</param>
/// <param name="serializer">An object serializer for serialization of objects in the data store.</param>
public RoamingSettingsHelper(string userId, RoamingDataStore dataStore = RoamingDataStore.UserExtensions, bool autoSync = true, IObjectSerializer serializer = null)
Comment thread
nmetulev marked this conversation as resolved.
Outdated
{
// TODO: Infuse unique identifier from Graph registration into the storage name.
string dataStoreName = "communityToolkit.roamingSettings";

if (serializer == null)
{
serializer = new JsonObjectSerializer();
}

switch (dataStore)
{
case RoamingDataStore.UserExtensions:
DataStore = new UserExtensionDataStore(dataStoreName, userId, serializer);
break;

default:
throw new ArgumentOutOfRangeException(nameof(dataStore));
}

if (autoSync)
{
try
{
DataStore.Sync();
}
catch
{
// Sync may fail if the storage container does not yet exist.
Comment thread
shweaver-MSFT marked this conversation as resolved.
Outdated
}
}
}

/// <summary>
/// An indexer for easily accessing key values.
/// </summary>
/// <param name="key">The key for the desired value.</param>
/// <returns>The value found for the provided key.</returns>
public object this[string key]
{
get => DataStore.Read<object>(key);
set
{
if (DataStore.KeyExists(key))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if it doesn't exist, shouldn't it create the key?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ya, good point. I think it should.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree - will you do that in this PR?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done :)

{
DataStore.Save(key, value);
}
}
}

/// <inheritdoc />
public Task<bool> FileExistsAsync(string filePath) => DataStore.FileExistsAsync(filePath);

/// <inheritdoc />
public bool KeyExists(string key) => DataStore.KeyExists(key);

/// <inheritdoc />
public bool KeyExists(string compositeKey, string key) => DataStore.KeyExists(compositeKey, key);

/// <inheritdoc />
public T Read<T>(string key, T @default = default) => DataStore.Read<T>(key, @default);

/// <inheritdoc />
public T Read<T>(string compositeKey, string key, T @default = default) => DataStore.Read(compositeKey, key, @default);

/// <inheritdoc />
public Task<T> ReadFileAsync<T>(string filePath, T @default = default) => DataStore.ReadFileAsync(filePath, @default);

/// <inheritdoc />
public void Save<T>(string key, T value) => DataStore.Save<T>(key, value);

/// <inheritdoc />
public void Save<T>(string compositeKey, IDictionary<string, T> values) => DataStore.Save<T>(compositeKey, values);

/// <inheritdoc />
public Task<StorageFile> SaveFileAsync<T>(string filePath, T value) => DataStore.SaveFileAsync<T>(filePath, value);

/// <summary>
/// Create a new storage container.
/// </summary>
/// <returns>A Task.</returns>
public Task Create() => DataStore.Create();

/// <summary>
/// Delete the existing storage container.
/// </summary>
/// <returns>A Task.</returns>
public Task Delete() => DataStore.Delete();

/// <summary>
/// Syncronize the internal cache with the remote storage endpoint.
/// </summary>
/// <returns>A Task.</returns>
public Task Sync() => DataStore.Sync();
}
}
Loading