diff --git a/CommunityToolkit.Graph.Uwp/Helpers/RoamingSettings/BaseRoamingSettingsDataStore.cs b/CommunityToolkit.Graph.Uwp/Helpers/RoamingSettings/BaseRoamingSettingsDataStore.cs
deleted file mode 100644
index 844b7ee..0000000
--- a/CommunityToolkit.Graph.Uwp/Helpers/RoamingSettings/BaseRoamingSettingsDataStore.cs
+++ /dev/null
@@ -1,303 +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.Collections.Generic;
-using System.Linq;
-using System.Reflection;
-using System.Threading.Tasks;
-using Microsoft.Toolkit.Uwp.Helpers;
-using Windows.Storage;
-
-namespace CommunityToolkit.Graph.Uwp.Helpers.RoamingSettings
-{
- ///
- /// A base class for easily building roaming settings helper implementations.
- ///
- public abstract class BaseRoamingSettingsDataStore : IRoamingSettingsDataStore
- {
- ///
- public EventHandler SyncCompleted { get; set; }
-
- ///
- public EventHandler SyncFailed { get; set; }
-
- ///
- public bool AutoSync { get; }
-
- ///
- public string Id { get; }
-
- ///
- public string UserId { get; }
-
- ///
- public IDictionary Cache { get; private set; } = new Dictionary();
-
- ///
- /// Gets an object serializer for converting objects in the data store.
- ///
- protected IObjectSerializer Serializer { get; }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The id of the target Graph user.
- /// A unique id for the data store.
- /// An IObjectSerializer used for serializing objects.
- /// Determines if the data store should sync for every interaction.
- public BaseRoamingSettingsDataStore(string userId, string dataStoreId, IObjectSerializer objectSerializer, bool autoSync = true)
- {
- AutoSync = autoSync;
- Id = dataStoreId;
- UserId = userId;
- Serializer = objectSerializer;
- }
-
- ///
- /// Create a new instance of the data storage container.
- ///
- /// A task.
- public abstract Task Create();
-
- ///
- /// Delete the instance of the data storage container.
- ///
- /// A task.
- public abstract Task Delete();
-
- ///
- /// Determines whether a setting already exists.
- ///
- /// Key of the setting (that contains object).
- /// True if a value exists.
- public bool KeyExists(string key)
- {
- return Cache?.ContainsKey(key) ?? false;
- }
-
- ///
- /// Determines whether a setting already exists in composite.
- ///
- /// Key of the composite (that contains settings).
- /// Key of the setting (that contains object).
- /// True if a value exists.
- public bool KeyExists(string compositeKey, string key)
- {
- if (KeyExists(compositeKey))
- {
- ApplicationDataCompositeValue composite = (ApplicationDataCompositeValue)Cache[compositeKey];
- if (composite != null)
- {
- return composite.ContainsKey(key);
- }
- }
-
- return false;
- }
-
- ///
- /// Retrieves a single item by its key.
- ///
- /// Key of the object.
- /// Default value of the object.
- /// Type of object retrieved.
- /// The T object.
- public T Read(string key, T @default = default)
- {
- if (Cache != null && Cache.TryGetValue(key, out object value))
- {
- return DeserializeValue(value);
- }
-
- return @default;
- }
-
- ///
- /// Retrieves a single item by its key in composite.
- ///
- /// Key of the composite (that contains settings).
- /// Key of the object.
- /// Default value of the object.
- /// Type of object retrieved.
- /// The T object.
- public T Read(string compositeKey, string key, T @default = default)
- {
- if (Cache != null)
- {
- ApplicationDataCompositeValue composite = (ApplicationDataCompositeValue)Cache[compositeKey];
- if (composite != null)
- {
- object value = composite[key];
- if (value != null)
- {
- return DeserializeValue(value);
- }
- }
- }
-
- return @default;
- }
-
- ///
- /// Saves a single item by its key.
- ///
- /// Key of the value saved.
- /// Object to save.
- /// Type of object saved.
- public void Save(string key, T value)
- {
- // Update the cache
- Cache[key] = SerializeValue(value);
-
- if (AutoSync)
- {
- // Update the remote
- Task.Run(() => Sync());
- }
- }
-
- ///
- /// Saves a group of items by its key in a composite. This method should be considered
- /// for objects that do not exceed 8k bytes during the lifetime of the application
- /// (refers to Microsoft.Toolkit.Uwp.Helpers.IObjectStorageHelper.SaveFileAsync``1(System.String,``0)
- /// for complex/large objects) and for groups of settings which need to be treated
- /// in an atomic way.
- ///
- /// Key of the composite (that contains settings).
- /// Objects to save.
- /// Type of object saved.
- public void Save(string compositeKey, IDictionary values)
- {
- var type = typeof(T);
- var typeInfo = type.GetTypeInfo();
-
- if (KeyExists(compositeKey))
- {
- ApplicationDataCompositeValue composite = (ApplicationDataCompositeValue)Cache[compositeKey];
-
- foreach (KeyValuePair setting in values.ToList())
- {
- string key = setting.Key;
- object value = SerializeValue(setting.Value);
- if (composite.ContainsKey(setting.Key))
- {
- composite[key] = value;
- }
- else
- {
- composite.Add(key, value);
- }
- }
-
- // Update the cache
- Cache[compositeKey] = composite;
-
- if (AutoSync)
- {
- // Update the remote
- Task.Run(() => Sync());
- }
- }
- else
- {
- ApplicationDataCompositeValue composite = new ApplicationDataCompositeValue();
- foreach (KeyValuePair setting in values.ToList())
- {
- string key = setting.Key;
- object value = SerializeValue(setting.Value);
- composite.Add(key, value);
- }
-
- // Update the cache
- Cache[compositeKey] = composite;
-
- if (AutoSync)
- {
- // Update the remote
- Task.Run(() => Sync());
- }
- }
- }
-
- ///
- /// Determines whether a file already exists.
- ///
- /// Key of the file (that contains object).
- /// True if a value exists.
- public abstract Task FileExistsAsync(string filePath);
-
- ///
- /// Retrieves an object from a file.
- ///
- /// Path to the file that contains the object.
- /// Default value of the object.
- /// Type of object retrieved.
- /// Waiting task until completion with the object in the file.
- public abstract Task ReadFileAsync(string filePath, T @default = default);
-
- ///
- /// Saves an object inside a file.
- ///
- /// Path to the file that will contain the object.
- /// Object to save.
- /// Type of object saved.
- /// Waiting task until completion.
- public abstract Task SaveFileAsync(string filePath, T value);
-
- ///
- public abstract Task Sync();
-
- ///
- /// Delete the internal cache.
- ///
- protected void DeleteCache()
- {
- Cache.Clear();
- }
-
- ///
- /// Use the serializer to deserialize a value appropriately for the type.
- ///
- /// The type of object expected.
- /// The value to deserialize.
- /// An object of type T.
- protected T DeserializeValue(object value)
- {
- try
- {
- return Serializer.Deserialize((string)value);
- }
- catch
- {
- // Primitive types can't be deserialized.
- return (T)Convert.ChangeType(value, typeof(T));
- }
- }
-
- ///
- /// Use the serializer to serialize a value appropriately for the type.
- ///
- /// The type of object being serialized.
- /// The object to serialize.
- /// The serialized object.
- protected object SerializeValue(T value)
- {
- var type = typeof(T);
- var typeInfo = type.GetTypeInfo();
-
- // Skip serialization for primitives.
- if (typeInfo.IsPrimitive || type == typeof(string))
- {
- // Update the cache
- return value;
- }
- else
- {
- // Update the cache
- return Serializer.Serialize(value);
- }
- }
- }
-}
diff --git a/CommunityToolkit.Graph.Uwp/Helpers/RoamingSettings/IRoamingSettingsDataStore.cs b/CommunityToolkit.Graph.Uwp/Helpers/RoamingSettings/IRoamingSettingsDataStore.cs
deleted file mode 100644
index 00186ed..0000000
--- a/CommunityToolkit.Graph.Uwp/Helpers/RoamingSettings/IRoamingSettingsDataStore.cs
+++ /dev/null
@@ -1,65 +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.Collections.Generic;
-using System.Threading.Tasks;
-using Microsoft.Toolkit.Uwp.Helpers;
-
-namespace CommunityToolkit.Graph.Uwp.Helpers.RoamingSettings
-{
- ///
- /// Defines the contract for creating storage containers used for roaming data.
- ///
- public interface IRoamingSettingsDataStore : IObjectStorageHelper
- {
- ///
- /// Gets a value indicating whether the values should immediately sync or not.
- ///
- bool AutoSync { get; }
-
- ///
- /// Gets access to the key/value pairs cache directly.
- ///
- IDictionary Cache { get; }
-
- ///
- /// Gets the id of the data store.
- ///
- string Id { get; }
-
- ///
- /// Gets the id of the target user.
- ///
- string UserId { get; }
-
- ///
- /// Gets or sets an event handler for when a remote data sync completes successfully.
- ///
- EventHandler SyncCompleted { get; set; }
-
- ///
- /// Gets or sets an event handler for when a remote data sync fails.
- ///
- EventHandler SyncFailed { get; set; }
-
- ///
- /// Create a new storage container.
- ///
- /// A Task.
- Task Create();
-
- ///
- /// Delete the existing storage container.
- ///
- /// A Task.
- Task Delete();
-
- ///
- /// Syncronize the internal cache with the remote storage endpoint.
- ///
- /// A Task.
- Task Sync();
- }
-}
\ No newline at end of file
diff --git a/CommunityToolkit.Graph.Uwp/Helpers/RoamingSettings/OneDriveDataSource.cs b/CommunityToolkit.Graph.Uwp/Helpers/RoamingSettings/OneDriveDataSource.cs
deleted file mode 100644
index 9b95c99..0000000
--- a/CommunityToolkit.Graph.Uwp/Helpers/RoamingSettings/OneDriveDataSource.cs
+++ /dev/null
@@ -1,69 +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.IO;
-using System.Text;
-using System.Threading.Tasks;
-using CommunityToolkit.Authentication;
-using CommunityToolkit.Graph.Extensions;
-using Microsoft.Graph;
-using Microsoft.Toolkit.Uwp.Helpers;
-
-namespace CommunityToolkit.Graph.Uwp.Helpers.RoamingSettings
-{
- ///
- /// Helpers for interacting with files in the special OneDrive AppRoot folder.
- ///
- internal static class OneDriveDataSource
- {
- private static GraphServiceClient Graph => ProviderManager.Instance.GlobalProvider?.GetClient();
-
- // Create a new file.
- // This fails, because OneDrive doesn't like empty files. Use Update instead.
- // public static async Task Create(string fileWithExt)
- // {
- // var driveItem = new DriveItem()
- // {
- // Name = fileWithExt,
- // };
- // await Graph.Users[userId].Drive.Special.AppRoot.ItemWithPath(fileWithExt).Request().CreateAsync(driveItem);
- // }
-
- ///
- /// Updates or create a new file on the remote with the provided content.
- ///
- /// The type of object to save.
- /// A representing the asynchronous operation.
- public static async Task Update(string userId, string fileWithExt, T fileContents, IObjectSerializer serializer)
- {
- var json = serializer.Serialize(fileContents) as string;
- using var stream = new MemoryStream(Encoding.UTF8.GetBytes(json));
-
- return await Graph.Users[userId].Drive.Special.AppRoot.ItemWithPath(fileWithExt).Content.Request().PutAsync(stream);
- }
-
- ///
- /// Get a file from the remote.
- ///
- /// The type of object to return.
- /// A representing the asynchronous operation.
- public static async Task Retrieve(string userId, string fileWithExt, IObjectSerializer serializer)
- {
- Stream stream = await Graph.Users[userId].Drive.Special.AppRoot.ItemWithPath(fileWithExt).Content.Request().GetAsync();
-
- string streamContents = new StreamReader(stream).ReadToEnd();
-
- return serializer.Deserialize(streamContents);
- }
-
- ///
- /// Delete the file from the remote.
- ///
- /// A representing the asynchronous operation.
- public static async Task Delete(string userId, string fileWithExt)
- {
- await Graph.Users[userId].Drive.Special.AppRoot.ItemWithPath(fileWithExt).Request().DeleteAsync();
- }
- }
-}
diff --git a/CommunityToolkit.Graph.Uwp/Helpers/RoamingSettings/OneDriveDataStore.cs b/CommunityToolkit.Graph.Uwp/Helpers/RoamingSettings/OneDriveDataStore.cs
deleted file mode 100644
index b1f95bd..0000000
--- a/CommunityToolkit.Graph.Uwp/Helpers/RoamingSettings/OneDriveDataStore.cs
+++ /dev/null
@@ -1,154 +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.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using Microsoft.Toolkit.Uwp.Helpers;
-using Windows.Storage;
-
-namespace CommunityToolkit.Graph.Uwp.Helpers.RoamingSettings
-{
- ///
- /// A DataStore for managing roaming settings in OneDrive.
- ///
- public class OneDriveDataStore : BaseRoamingSettingsDataStore
- {
- ///
- /// Retrieve an object stored in a OneDrive file.
- ///
- /// The type of object to retrieve.
- /// The id of the target Graph user.
- /// The name of the file.
- /// An object serializer for handling deserialization.
- /// The deserialized file contents.
- public static async Task Get(string userId, string fileName, IObjectSerializer serializer)
- {
- return await OneDriveDataSource.Retrieve(userId, fileName, serializer);
- }
-
- ///
- /// Update the contents of a OneDrive file.
- ///
- /// The type of object being stored.
- /// The id of the target Graph user.
- /// The name of the file.
- /// The object to store.
- /// An object serializer for handling serialization.
- /// A task.
- public static async Task Set(string userId, string fileName, T fileContents, IObjectSerializer serializer)
- {
- await OneDriveDataSource.Update(userId, fileName, fileContents, serializer);
- }
-
- ///
- /// Delete a file from OneDrive by name.
- ///
- /// The id of the target Graph user.
- /// The name of the file.
- /// A task.
- public static async Task Delete(string userId, string fileName)
- {
- await OneDriveDataSource.Delete(userId, fileName);
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- public OneDriveDataStore(string userId, string syncDataFileName, IObjectSerializer objectSerializer, bool autoSync = true)
- : base(userId, syncDataFileName, objectSerializer, autoSync)
- {
- }
-
- ///
- public override Task Create()
- {
- return Task.CompletedTask;
- }
-
- ///
- public override async Task Delete()
- {
- // Clear the cache
- Cache.Clear();
-
- // Delete the remote.
- await Delete(UserId, Id);
- }
-
- ///
- public override async Task FileExistsAsync(string filePath)
- {
- var roamingSettings = await Get(UserId, Id, Serializer);
- return roamingSettings != null;
- }
-
- ///
- public override async Task ReadFileAsync(string filePath, T @default = default)
- {
- return await Get(UserId, filePath, Serializer) ?? @default;
- }
-
- ///
- public override async Task SaveFileAsync(string filePath, T value)
- {
- await Set(UserId, filePath, value, Serializer);
-
- // Can't convert DriveItem to StorageFile, so we return null instead.
- return null;
- }
-
- ///
- public override async Task Sync()
- {
- try
- {
- // Get the remote
- string fileName = Id;
- IDictionary remoteData = null;
- try
- {
- remoteData = await Get>(UserId, fileName, Serializer);
- }
- catch
- {
- // If get fails, we know the remote store does not exist.
- }
-
- bool needsUpdate = false;
- if (remoteData != null)
- {
- // Update local cache with additions from remote
- foreach (string key in remoteData.Keys.ToList())
- {
- // Only insert new values. Existing keys should be overwritten on the remote.
- if (!Cache.ContainsKey(key))
- {
- Cache.Add(key, remoteData[key]);
- needsUpdate = true;
- }
- }
- }
- else if (Cache.Count > 0)
- {
- // The remote does not yet exist, and we have data to save.
- needsUpdate = true;
- }
-
- if (needsUpdate)
- {
- // Send updates for local values, overwriting the remote.
- await Set(UserId, fileName, Cache, Serializer);
- }
-
- SyncCompleted?.Invoke(this, new EventArgs());
- }
- catch
- {
- SyncFailed?.Invoke(this, new EventArgs());
- }
- }
- }
-}
diff --git a/CommunityToolkit.Graph.Uwp/Helpers/RoamingSettings/RoamingSettingsHelper.cs b/CommunityToolkit.Graph.Uwp/Helpers/RoamingSettings/RoamingSettingsHelper.cs
deleted file mode 100644
index badd0c5..0000000
--- a/CommunityToolkit.Graph.Uwp/Helpers/RoamingSettings/RoamingSettingsHelper.cs
+++ /dev/null
@@ -1,188 +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.Collections.Generic;
-using System.Threading.Tasks;
-using CommunityToolkit.Authentication;
-using CommunityToolkit.Graph.Extensions;
-using Microsoft.Toolkit.Uwp.Helpers;
-using Windows.Storage;
-
-namespace CommunityToolkit.Graph.Uwp.Helpers.RoamingSettings
-{
- ///
- /// An enumeration of the available data storage methods for roaming data.
- ///
- public enum RoamingDataStore
- {
- ///
- /// Store data using open extensions on the Graph User.
- ///
- UserExtensions,
-
- ///
- /// Store data in a Graph User's OneDrive.
- ///
- OneDrive,
- }
-
- ///
- /// A helper class for syncing data to roaming data store.
- ///
- public class RoamingSettingsHelper : IRoamingSettingsDataStore
- {
- ///
- /// Gets the internal data store instance.
- ///
- public IRoamingSettingsDataStore DataStore { get; private set; }
-
- ///
- public EventHandler SyncCompleted { get; set; }
-
- ///
- public EventHandler SyncFailed { get; set; }
-
- ///
- public bool AutoSync => DataStore.AutoSync;
-
- ///
- public IDictionary Cache => DataStore.Cache;
-
- ///
- public string Id => DataStore.Id;
-
- ///
- public string UserId => DataStore.UserId;
-
- ///
- /// Creates a new RoamingSettingsHelper instance for the currently signed in user.
- ///
- /// Which specific data store is being used.
- /// Whether the values should immediately sync or not.
- /// Whether the values should immediately sync on change or wait until Sync is called explicitly.
- /// An object serializer for serialization of objects in the data store.
- /// A new instance of the RoamingSettingsHelper configured for the current user.
- public static async Task CreateForCurrentUser(RoamingDataStore dataStore = RoamingDataStore.UserExtensions, bool syncOnInit = true, 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.GetClient().Me.Request().GetAsync();
- return new RoamingSettingsHelper(me.Id, dataStore, syncOnInit, autoSync, serializer);
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The id of the target Graph User.
- /// Which specific data store is being used.
- /// Whether the values should immediately sync or not.
- /// Whether the values should immediately sync on change or wait until Sync is called explicitly.
- /// An object serializer for serialization of objects in the data store.
- public RoamingSettingsHelper(string userId, RoamingDataStore dataStore = RoamingDataStore.UserExtensions, bool syncOnInit = true, bool autoSync = true, IObjectSerializer serializer = null)
- {
- // TODO: Infuse unique identifier from Graph registration into the storage name.
- string dataStoreName = "communityToolkit.roamingSettings";
-
- if (serializer == null)
- {
- serializer = new SystemSerializer();
- }
-
- switch (dataStore)
- {
- case RoamingDataStore.UserExtensions:
- DataStore = new UserExtensionDataStore(userId, dataStoreName, serializer, autoSync);
- break;
-
- case RoamingDataStore.OneDrive:
- DataStore = new OneDriveDataStore(userId, dataStoreName, serializer, autoSync);
- break;
-
- default:
- throw new ArgumentOutOfRangeException(nameof(dataStore));
- }
-
- DataStore.SyncCompleted += (s, e) => SyncCompleted?.Invoke(this, e);
- DataStore.SyncFailed += (s, e) => SyncFailed?.Invoke(this, e);
-
- if (syncOnInit)
- {
- _ = Sync();
- }
- }
-
- ///
- /// An indexer for easily accessing key values.
- ///
- /// The key for the desired value.
- /// The value found for the provided key.
- public object this[string key]
- {
- get => DataStore.Read(key);
- set => DataStore.Save(key, value);
- }
-
- ///
- public Task FileExistsAsync(string filePath) => DataStore.FileExistsAsync(filePath);
-
- ///
- public bool KeyExists(string key) => DataStore.KeyExists(key);
-
- ///
- public bool KeyExists(string compositeKey, string key) => DataStore.KeyExists(compositeKey, key);
-
- ///
- public T Read(string key, T @default = default) => DataStore.Read(key, @default);
-
- ///
- public T Read(string compositeKey, string key, T @default = default) => DataStore.Read(compositeKey, key, @default);
-
- ///
- public Task ReadFileAsync(string filePath, T @default = default) => DataStore.ReadFileAsync(filePath, @default);
-
- ///
- public void Save(string key, T value) => DataStore.Save(key, value);
-
- ///
- public void Save(string compositeKey, IDictionary values) => DataStore.Save(compositeKey, values);
-
- ///
- public Task SaveFileAsync(string filePath, T value) => DataStore.SaveFileAsync(filePath, value);
-
- ///
- /// Create a new storage container.
- ///
- /// A Task.
- public Task Create() => DataStore.Create();
-
- ///
- /// Delete the existing storage container.
- ///
- /// A Task.
- public Task Delete() => DataStore.Delete();
-
- ///
- /// Syncronize the internal cache with the remote storage endpoint.
- ///
- /// A Task.
- public async Task Sync()
- {
- try
- {
- await DataStore.Sync();
- }
- catch
- {
- // Sync may fail if the storage container does not yet exist.
- await DataStore.Create();
- await DataStore.Sync();
- }
- }
- }
-}
\ No newline at end of file
diff --git a/CommunityToolkit.Graph.Uwp/Helpers/RoamingSettings/UserExtensionDataStore.cs b/CommunityToolkit.Graph.Uwp/Helpers/RoamingSettings/UserExtensionDataStore.cs
deleted file mode 100644
index d450d81..0000000
--- a/CommunityToolkit.Graph.Uwp/Helpers/RoamingSettings/UserExtensionDataStore.cs
+++ /dev/null
@@ -1,201 +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.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using Microsoft.Graph;
-using Microsoft.Toolkit.Uwp.Helpers;
-using Windows.Storage;
-
-namespace CommunityToolkit.Graph.Uwp.Helpers.RoamingSettings
-{
- ///
- /// An IObjectStorageHelper implementation using open extensions on the Graph User for storing key/value pairs.
- ///
- public class UserExtensionDataStore : BaseRoamingSettingsDataStore
- {
- ///
- /// Retrieve the value from Graph User extensions and cast the response to the provided type.
- ///
- /// The type to cast the return result to.
- /// The id of the user.
- /// The id of the user extension.
- /// The key for the desired value.
- /// The value from the data store.
- public static async Task Get(string userId, string extensionId, string key)
- {
- return (T)await Get(userId, extensionId, key);
- }
-
- ///
- /// Retrieve the value from Graph User extensions by extensionId, userId, and key.
- ///
- /// The id of the user.
- /// The id of the user extension.
- /// The key for the desired value.
- /// The value from the data store.
- public static async Task Get(string userId, string extensionId, string key)
- {
- var userExtension = await GetExtensionForUser(userId, extensionId);
- return userExtension.AdditionalData[key];
- }
-
- ///
- /// Set a value by key in a Graph User's extension.
- ///
- /// The id of the user.
- /// The id of the user extension.
- /// The key for the target value.
- /// The value to set.
- /// A task upon completion.
- public static async Task Set(string userId, string extensionId, string key, object value)
- {
- await UserExtensionsDataSource.SetValue(userId, extensionId, key, value);
- }
-
- ///
- /// Creates a new roaming settings extension on a Graph User.
- ///
- /// The id of the user.
- /// The id of the user extension.
- /// The newly created user extension.
- public static async Task Create(string userId, string extensionId)
- {
- var userExtension = await UserExtensionsDataSource.CreateExtension(userId, extensionId);
- return userExtension;
- }
-
- ///
- /// Deletes an extension by id on a Graph User.
- ///
- /// The id of the user.
- /// The id of the user extension.
- /// A task upon completion.
- public static async Task Delete(string userId, string extensionId)
- {
- await UserExtensionsDataSource.DeleteExtension(userId, extensionId);
- }
-
- ///
- /// Retrieves a user extension.
- ///
- /// The id of the user.
- /// The id of the user extension.
- /// The target extension.
- public static async Task GetExtensionForUser(string userId, string extensionId)
- {
- var userExtension = await UserExtensionsDataSource.GetExtension(userId, extensionId);
- return userExtension;
- }
-
- private static readonly IList ReservedKeys = new List { "responseHeaders", "statusCode", "@odata.context" };
-
- ///
- /// Initializes a new instance of the class.
- ///
- public UserExtensionDataStore(string userId, string extensionId, IObjectSerializer objectSerializer, bool autoSync = true)
- : base(userId, extensionId, objectSerializer, autoSync)
- {
- }
-
- ///
- /// Creates a new roaming settings extension on the Graph User.
- ///
- /// The newly created Extension object.
- public override async Task Create()
- {
- await Create(UserId, Id);
- }
-
- ///
- /// Deletes the roamingSettings extension from the Graph User.
- ///
- /// A void task.
- public override async Task Delete()
- {
- // Delete the cache
- Cache.Clear();
-
- // Delete the remote.
- await Delete(UserId, Id);
- }
-
- ///
- /// Update the remote extension to match the local cache and retrieve any new keys. Any existing remote values are replaced.
- ///
- /// The freshly synced user extension.
- public override async Task Sync()
- {
- try
- {
- IDictionary remoteData = null;
-
- try
- {
- // Get the remote
- Extension extension = await GetExtensionForUser(UserId, Id);
- remoteData = extension.AdditionalData;
- }
- catch
- {
- }
-
- if (Cache != null)
- {
- // Send updates for all local values, overwriting the remote.
- foreach (string key in Cache.Keys.ToList())
- {
- if (ReservedKeys.Contains(key))
- {
- continue;
- }
-
- if (remoteData == null || !remoteData.ContainsKey(key) || !EqualityComparer.Default.Equals(remoteData[key], Cache[key]))
- {
- Save(key, Cache[key]);
- }
- }
- }
-
- if (remoteData != null)
- {
- // Update local cache with additions from remote
- foreach (string key in remoteData.Keys.ToList())
- {
- if (!Cache.ContainsKey(key))
- {
- Cache.Add(key, remoteData[key]);
- }
- }
- }
-
- SyncCompleted?.Invoke(this, new EventArgs());
- }
- catch
- {
- SyncFailed?.Invoke(this, new EventArgs());
- }
- }
-
- ///
- public override Task FileExistsAsync(string filePath)
- {
- throw new NotImplementedException();
- }
-
- ///
- public override Task ReadFileAsync(string filePath, T @default = default)
- {
- throw new NotImplementedException();
- }
-
- ///
- public override Task SaveFileAsync(string filePath, T value)
- {
- throw new NotImplementedException();
- }
- }
-}
\ No newline at end of file
diff --git a/CommunityToolkit.Graph/CommunityToolkit.Graph.csproj b/CommunityToolkit.Graph/CommunityToolkit.Graph.csproj
index 0f03508..3af7782 100644
--- a/CommunityToolkit.Graph/CommunityToolkit.Graph.csproj
+++ b/CommunityToolkit.Graph/CommunityToolkit.Graph.csproj
@@ -10,10 +10,12 @@
- ProviderExtensions: Extension on IProvider for accessing a pre-configured GraphServiceClient instance.
Windows Community Toolkit Graph Provider Extensions
+ 9.0
+
diff --git a/CommunityToolkit.Graph/Helpers/RoamingSettings/IRemoteSettingsStorageHelper.cs b/CommunityToolkit.Graph/Helpers/RoamingSettings/IRemoteSettingsStorageHelper.cs
new file mode 100644
index 0000000..66601d0
--- /dev/null
+++ b/CommunityToolkit.Graph/Helpers/RoamingSettings/IRemoteSettingsStorageHelper.cs
@@ -0,0 +1,35 @@
+// 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.Text;
+using System.Threading.Tasks;
+using Microsoft.Toolkit.Helpers;
+
+namespace CommunityToolkit.Graph.Helpers.RoamingSettings
+{
+ ///
+ /// Describes a remote settings storage location with basic sync support.
+ ///
+ /// The type of keys to use for accessing values.
+ public interface IRemoteSettingsStorageHelper : ISettingsStorageHelper
+ {
+ ///
+ /// Gets or sets an event that fires whenever a sync request has completed.
+ ///
+ EventHandler SyncCompleted { get; set; }
+
+ ///
+ /// Gets or sets a value an event that fires whenever a remote sync request has failed.
+ ///
+ EventHandler SyncFailed { get; set; }
+
+ ///
+ /// Update the remote extension to match the local cache and retrieve any new keys. Any existing remote values are replaced.
+ ///
+ /// The freshly synced user extension.
+ Task Sync();
+ }
+}
diff --git a/CommunityToolkit.Graph/Helpers/RoamingSettings/OneDriveDataSource.cs b/CommunityToolkit.Graph/Helpers/RoamingSettings/OneDriveDataSource.cs
new file mode 100644
index 0000000..4f4c686
--- /dev/null
+++ b/CommunityToolkit.Graph/Helpers/RoamingSettings/OneDriveDataSource.cs
@@ -0,0 +1,99 @@
+// 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.IO;
+using System.Text;
+using System.Threading.Tasks;
+using CommunityToolkit.Authentication;
+using CommunityToolkit.Graph.Extensions;
+using Microsoft.Graph;
+using Microsoft.Toolkit.Helpers;
+
+namespace CommunityToolkit.Graph.Helpers.RoamingSettings
+{
+ ///
+ /// Helpers for interacting with files in the special OneDrive AppRoot folder.
+ ///
+ internal static class OneDriveDataSource
+ {
+ private static GraphServiceClient Graph => ProviderManager.Instance.GlobalProvider?.GetClient();
+
+ ///
+ /// Updates or create a new file on the remote with the provided content.
+ ///
+ /// The type of object to save.
+ /// A representing the asynchronous operation.
+ public static async Task SetFileAsync(string userId, string itemPath, T fileContents, IObjectSerializer serializer)
+ {
+ var json = serializer.Serialize(fileContents) as string;
+ using var stream = new MemoryStream(Encoding.UTF8.GetBytes(json));
+
+ return await Graph.Users[userId].Drive.Special.AppRoot.ItemWithPath(itemPath).Content.Request().PutAsync(stream);
+ }
+
+ ///
+ /// Get a file from the remote.
+ ///
+ /// The type of object to return.
+ /// A representing the asynchronous operation.
+ public static async Task GetFileAsync(string userId, string itemPath, IObjectSerializer serializer)
+ {
+ Stream stream = await Graph.Users[userId].Drive.Special.AppRoot.ItemWithPath(itemPath).Content.Request().GetAsync();
+
+ string streamContents = new StreamReader(stream).ReadToEnd();
+
+ return serializer.Deserialize(streamContents);
+ }
+
+ ///
+ /// Delete the file from the remote.
+ ///
+ /// A representing the asynchronous operation.
+ public static async Task DeleteItemAsync(string userId, string itemPath)
+ {
+ await Graph.Users[userId].Drive.Special.AppRoot.ItemWithPath(itemPath).Request().DeleteAsync();
+ }
+
+ public static async Task CreateFolderAsync(string userId, string folderName, string path = null)
+ {
+ var folderDriveItem = new DriveItem()
+ {
+ Name = folderName,
+ Folder = new Folder(),
+ };
+
+ if (path != null)
+ {
+ await Graph.Users[userId].Drive.Special.AppRoot.ItemWithPath(path).Children.Request().AddAsync(folderDriveItem);
+ }
+ else
+ {
+ await Graph.Users[userId].Drive.Special.AppRoot.Children.Request().AddAsync(folderDriveItem);
+ }
+ }
+
+ public static async Task> ReadFolderAsync(string userId, string folderPath)
+ {
+ IDriveItemChildrenCollectionPage folderContents = await Graph.Users[userId].Drive.Special.AppRoot.ItemWithPath(folderPath).Children.Request().GetAsync();
+
+ var results = new List<(DirectoryItemType, string)>();
+ foreach (var item in folderContents)
+ {
+ var itemType = (item.Folder != null)
+ ? DirectoryItemType.Folder
+ : item.Size != null
+ ? DirectoryItemType.File
+ : DirectoryItemType.None;
+
+ var itemName = item.Name;
+
+ results.Add((itemType, itemName));
+ }
+
+ return results;
+ }
+ }
+}
diff --git a/CommunityToolkit.Graph/Helpers/RoamingSettings/OneDriveStorageHelper.cs b/CommunityToolkit.Graph/Helpers/RoamingSettings/OneDriveStorageHelper.cs
new file mode 100644
index 0000000..90e4259
--- /dev/null
+++ b/CommunityToolkit.Graph/Helpers/RoamingSettings/OneDriveStorageHelper.cs
@@ -0,0 +1,102 @@
+// 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.Reflection;
+using System.Threading.Tasks;
+using CommunityToolkit.Authentication;
+using CommunityToolkit.Graph.Extensions;
+using Microsoft.Toolkit.Helpers;
+
+namespace CommunityToolkit.Graph.Helpers.RoamingSettings
+{
+ ///
+ /// A base class for easily building roaming settings helper implementations.
+ ///
+ public class OneDriveStorageHelper : IFileStorageHelper
+ {
+ ///
+ /// Gets the id of the Graph user.
+ ///
+ public string UserId { get; }
+
+ ///
+ /// Gets an object serializer for converting objects in the data store.
+ ///
+ public IObjectSerializer Serializer { get; }
+
+ ///
+ /// Creates a new instance using the userId retrieved from a Graph "Me" request.
+ ///
+ /// A serializer used for converting stored objects.
+ /// A new instance of the configured for the current Graph user.
+ public static async Task CreateForCurrentUserAsync(IObjectSerializer objectSerializer = null)
+ {
+ var provider = ProviderManager.Instance.GlobalProvider;
+ if (provider == null || provider.State != ProviderState.SignedIn)
+ {
+ throw new InvalidOperationException($"The {nameof(ProviderManager.GlobalProvider)} must be set and signed in to create a new {nameof(OneDriveStorageHelper)} for the current user.");
+ }
+
+ var me = await provider.GetClient().Me.Request().GetAsync();
+ var userId = me.Id;
+
+ return new OneDriveStorageHelper(userId, objectSerializer);
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The id of the target Graph user.
+ /// A serializer used for converting stored objects.
+ public OneDriveStorageHelper(string userId, IObjectSerializer objectSerializer = null)
+ {
+ UserId = userId ?? throw new ArgumentNullException(nameof(userId));
+ Serializer = objectSerializer ?? new SystemSerializer();
+ }
+
+ ///
+ public async Task ReadFileAsync(string filePath, T @default = default)
+ {
+ return await OneDriveDataSource.GetFileAsync(UserId, filePath, Serializer) ?? @default;
+ }
+
+ ///
+ public Task> ReadFolderAsync(string folderPath)
+ {
+ return OneDriveDataSource.ReadFolderAsync(UserId, folderPath);
+ }
+
+ ///
+ public async Task CreateFileAsync(string filePath, T value)
+ {
+ await OneDriveDataSource.SetFileAsync(UserId, filePath, value, Serializer);
+ }
+
+ ///
+ public Task CreateFolderAsync(string folderName)
+ {
+ return OneDriveDataSource.CreateFolderAsync(UserId, folderName);
+ }
+
+ ///
+ /// Ensure a folder exists at the path specified.
+ ///
+ /// The name of the new folder.
+ /// The path to create the new folder in.
+ /// A task.
+ public Task CreateFolderAsync(string folderName, string folderPath)
+ {
+ return OneDriveDataSource.CreateFolderAsync(UserId, folderName, folderPath);
+ }
+
+ ///
+ public Task DeleteItemAsync(string itemPath)
+ {
+ return OneDriveDataSource.DeleteItemAsync(UserId, itemPath);
+ }
+ }
+}
diff --git a/CommunityToolkit.Graph.Uwp/Helpers/RoamingSettings/UserExtensionsDataSource.cs b/CommunityToolkit.Graph/Helpers/RoamingSettings/UserExtensionDataSource.cs
similarity index 97%
rename from CommunityToolkit.Graph.Uwp/Helpers/RoamingSettings/UserExtensionsDataSource.cs
rename to CommunityToolkit.Graph/Helpers/RoamingSettings/UserExtensionDataSource.cs
index 61218c2..2a035b6 100644
--- a/CommunityToolkit.Graph.Uwp/Helpers/RoamingSettings/UserExtensionsDataSource.cs
+++ b/CommunityToolkit.Graph/Helpers/RoamingSettings/UserExtensionDataSource.cs
@@ -10,12 +10,12 @@
using CommunityToolkit.Graph.Extensions;
using Microsoft.Graph;
-namespace CommunityToolkit.Graph.Uwp.Helpers.RoamingSettings
+namespace CommunityToolkit.Graph.Helpers.RoamingSettings
{
///
/// Manages Graph interaction with open extensions on the user.
///
- internal static class UserExtensionsDataSource
+ internal static class UserExtensionDataSource
{
private static GraphServiceClient Graph => ProviderManager.Instance.GlobalProvider?.GetClient();
@@ -91,7 +91,7 @@ public static async Task CreateExtension(string userId, string extens
"\"extensionName\": \"" + extensionId + "\"," +
"}";
- HttpRequestMessage hrm = new HttpRequestMessage(HttpMethod.Post, requestUrl);
+ HttpRequestMessage hrm = new (HttpMethod.Post, requestUrl);
hrm.Content = new StringContent(json, System.Text.Encoding.UTF8, "application/json");
await Graph.AuthenticationProvider.AuthenticateRequestAsync(hrm);
HttpResponseMessage response = await Graph.HttpProvider.SendAsync(hrm);
diff --git a/CommunityToolkit.Graph/Helpers/RoamingSettings/UserExtensionStorageHelper.cs b/CommunityToolkit.Graph/Helpers/RoamingSettings/UserExtensionStorageHelper.cs
new file mode 100644
index 0000000..ee150ae
--- /dev/null
+++ b/CommunityToolkit.Graph/Helpers/RoamingSettings/UserExtensionStorageHelper.cs
@@ -0,0 +1,264 @@
+// 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.Reflection;
+using System.Threading;
+using System.Threading.Tasks;
+using CommunityToolkit.Authentication;
+using CommunityToolkit.Graph.Extensions;
+using Microsoft.Graph;
+using Microsoft.Toolkit.Extensions;
+using Microsoft.Toolkit.Helpers;
+
+namespace CommunityToolkit.Graph.Helpers.RoamingSettings
+{
+ ///
+ /// An IObjectStorageHelper implementation using open extensions on the Graph User for storing key/value pairs.
+ ///
+ public class UserExtensionStorageHelper : IRemoteSettingsStorageHelper
+ {
+ private static readonly IList ReservedKeys = new List { "responseHeaders", "statusCode", "@odata.context" };
+ private static readonly SemaphoreSlim SyncLock = new (1);
+
+ ///
+ /// Gets or sets an event that fires whenever a sync request has completed.
+ ///
+ public EventHandler SyncCompleted { get; set; }
+
+ ///
+ /// gets or sets an event that fires whenever a remote sync request has failed.
+ ///
+ public EventHandler SyncFailed { get; set; }
+
+ ///
+ /// Gets the id for the target extension on a Graph user.
+ ///
+ public string ExtensionId { get; }
+
+ ///
+ /// Gets the id of the target Graph user.
+ ///
+ public string UserId { get; }
+
+ ///
+ /// Gets an object serializer for converting objects in the data store.
+ ///
+ public IObjectSerializer Serializer { get; }
+
+ ///
+ /// Gets a cache of the stored values, converted using the provided serializer.
+ ///
+ public IReadOnlyDictionary Cache => _cache;
+
+ private readonly Dictionary _cache;
+ private bool _cleared;
+
+ ///
+ /// Creates a new instance using the userId retrieved from a Graph "Me" request.
+ ///
+ /// The id for the target extension on a Graph user.
+ /// A serializer used for converting stored objects.
+ /// A new instance of the configured for the current Graph user.
+ public static async Task CreateForCurrentUserAsync(string extensionId, IObjectSerializer objectSerializer = null)
+ {
+ if (extensionId == null)
+ {
+ throw new ArgumentNullException(nameof(extensionId));
+ }
+
+ var provider = ProviderManager.Instance.GlobalProvider;
+ if (provider == null || provider.State != ProviderState.SignedIn)
+ {
+ throw new InvalidOperationException($"The {nameof(ProviderManager.GlobalProvider)} must be set and signed in to create a new {nameof(UserExtensionStorageHelper)} for the current user.");
+ }
+
+ var me = await provider.GetClient().Me.Request().GetAsync();
+ var userId = me.Id;
+
+ return new UserExtensionStorageHelper(extensionId, userId, objectSerializer);
+ }
+
+ ///
+ /// An indexer for easily accessing key values.
+ ///
+ /// The key for the desired value.
+ /// The value found for the provided key.
+ public object this[string key]
+ {
+ get => ISettingsStorageHelperExtensions.Read(this, key);
+ set => Save(key, value);
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The id for the target extension on a Graph user.
+ /// The id of the target Graph user.
+ /// A serializer used for converting stored objects.
+ public UserExtensionStorageHelper(string extensionId, string userId, IObjectSerializer objectSerializer = null)
+ {
+ ExtensionId = extensionId ?? throw new ArgumentNullException(nameof(extensionId));
+ UserId = userId ?? throw new ArgumentNullException(nameof(userId));
+ Serializer = objectSerializer ?? new SystemSerializer();
+
+ _cache = new Dictionary();
+ _cleared = false;
+ }
+
+ ///
+ public void Save(string key, T value)
+ {
+ _cache[key] = SerializeValue(value);
+ }
+
+ ///
+ public bool TryRead(string key, out TValue value)
+ {
+ if (_cache.TryGetValue(key, out object cachedValue))
+ {
+ value = DeserializeValue(cachedValue);
+ return true;
+ }
+ else
+ {
+ value = default;
+ return false;
+ }
+ }
+
+ ///
+ public bool TryDelete(string key)
+ {
+ return _cache.Remove(key);
+ }
+
+ ///
+ public void Clear()
+ {
+ _cache.Clear();
+ _cleared = true;
+ }
+
+ ///
+ /// Synchronize the cache with the remote:
+ /// - If the cache has been cleared, the remote will be deleted and recreated.
+ /// - Any cached keys will be saved to the remote, overwriting existing values.
+ /// - Any new keys from the remote will be stored in the cache.
+ ///
+ /// The freshly synced user extension.
+ public virtual async Task Sync()
+ {
+ await SyncLock.WaitAsync();
+
+ try
+ {
+ IDictionary remoteData = null;
+
+ // Check if the extension should be cleared.
+ if (_cleared)
+ {
+ // Delete and re-create the remote extension.
+ await UserExtensionDataSource.DeleteExtension(UserId, ExtensionId);
+ Extension extension = await UserExtensionDataSource.CreateExtension(UserId, ExtensionId);
+ remoteData = extension.AdditionalData;
+
+ _cleared = false;
+ }
+ else
+ {
+ // Get the remote extension.
+ Extension extension = await UserExtensionDataSource.GetExtension(UserId, ExtensionId);
+ remoteData = extension.AdditionalData;
+ }
+
+ // Send updates for all local values, overwriting the remote.
+ foreach (string key in _cache.Keys.ToList())
+ {
+ if (ReservedKeys.Contains(key))
+ {
+ continue;
+ }
+
+ if (!remoteData.ContainsKey(key) || !EqualityComparer.Default.Equals(remoteData[key], Cache[key]))
+ {
+ Save(key, _cache[key]);
+ }
+ }
+
+ if (remoteData != null)
+ {
+ // Update local cache with additions from remote
+ foreach (string key in remoteData.Keys.ToList())
+ {
+ if (ReservedKeys.Contains(key))
+ {
+ continue;
+ }
+
+ if (!_cache.ContainsKey(key))
+ {
+ _cache.Add(key, remoteData[key]);
+ }
+ }
+ }
+
+ SyncCompleted?.Invoke(this, new EventArgs());
+ }
+ catch
+ {
+ SyncFailed?.Invoke(this, new EventArgs());
+ }
+ finally
+ {
+ SyncLock.Release();
+ }
+ }
+
+ ///
+ /// Use the serializer to deserialize a value appropriately for the type.
+ ///
+ /// The type of object expected.
+ /// The value to deserialize.
+ /// An object of type T.
+ protected T DeserializeValue(object value)
+ {
+ try
+ {
+ return Serializer.Deserialize((string)value);
+ }
+ catch
+ {
+ // Primitive types can't be deserialized.
+ return (T)Convert.ChangeType(value, typeof(T));
+ }
+ }
+
+ ///
+ /// Use the serializer to serialize a value appropriately for the type.
+ ///
+ /// The type of object being serialized.
+ /// The object to serialize.
+ /// The serialized object.
+ protected object SerializeValue(T value)
+ {
+ var type = typeof(T);
+ var typeInfo = type.GetTypeInfo();
+
+ // Skip serialization for primitives.
+ if (typeInfo.IsPrimitive || type == typeof(string))
+ {
+ // Update the cache
+ return value;
+ }
+ else
+ {
+ // Update the cache
+ return Serializer.Serialize(value);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/SampleTest/MainPage.xaml b/SampleTest/MainPage.xaml
index edd557b..a3a4ed7 100644
--- a/SampleTest/MainPage.xaml
+++ b/SampleTest/MainPage.xaml
@@ -52,9 +52,6 @@
-
-
-
PersonViewSample.xaml
-
- RoamingSettingsView.xaml
-
-
@@ -161,10 +157,6 @@
Designer
MSBuild:Compile
-
- MSBuild:Compile
- Designer
-
diff --git a/SampleTest/Samples/RoamingSettings/RoamingSettingsView.xaml b/SampleTest/Samples/RoamingSettings/RoamingSettingsView.xaml
deleted file mode 100644
index 398282c..0000000
--- a/SampleTest/Samples/RoamingSettings/RoamingSettingsView.xaml
+++ /dev/null
@@ -1,79 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/SampleTest/Samples/RoamingSettings/RoamingSettingsView.xaml.cs b/SampleTest/Samples/RoamingSettings/RoamingSettingsView.xaml.cs
deleted file mode 100644
index 66f2b4b..0000000
--- a/SampleTest/Samples/RoamingSettings/RoamingSettingsView.xaml.cs
+++ /dev/null
@@ -1,57 +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.Collections.Generic;
-using Windows.UI.Xaml.Controls;
-
-namespace SampleTest.Samples
-{
- ///
- /// A sample for demonstrating features in the RoamingSettings namespace.
- ///
- public sealed partial class RoamingSettingsView : Page
- {
- private RoamingSettingsViewModel _vm => DataContext as RoamingSettingsViewModel;
-
- public RoamingSettingsView()
- {
- InitializeComponent();
- DataContext = new RoamingSettingsViewModel();
- }
-
- private void CreateButton_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
- {
- _vm.CreateCustomRoamingSettings();
- }
-
- private void DeleteButton_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
- {
- _vm.DeleteCustomRoamingSettings();
- }
-
- private void SetButton_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
- {
- _vm.SetValue();
- }
-
- private void GetButton_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
- {
- _vm.GetValue();
- }
-
- private void ViewButton_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
- {
- _vm.SyncRoamingSettings();
- }
-
- private void AdditionalData_ItemClick(object sender, ItemClickEventArgs e)
- {
- if (e.ClickedItem is KeyValuePair kvp)
- {
- _vm.KeyInputText = kvp.Key;
- _vm.ValueInputText = kvp.Value.ToString();
- }
- }
- }
-}
diff --git a/SampleTest/Samples/RoamingSettings/RoamingSettingsViewModel.cs b/SampleTest/Samples/RoamingSettings/RoamingSettingsViewModel.cs
deleted file mode 100644
index 011c6b1..0000000
--- a/SampleTest/Samples/RoamingSettings/RoamingSettingsViewModel.cs
+++ /dev/null
@@ -1,195 +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 CommunityToolkit.Authentication;
-using CommunityToolkit.Graph.Uwp.Helpers.RoamingSettings;
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.ComponentModel;
-using System.Runtime.CompilerServices;
-using System.Threading.Tasks;
-
-namespace SampleTest.Samples
-{
- public class RoamingSettingsViewModel : INotifyPropertyChanged
- {
- private IProvider GlobalProvider => ProviderManager.Instance.GlobalProvider;
-
- public event PropertyChangedEventHandler PropertyChanged;
-
- private string _errorText;
- public string ErrorText
- {
- get => _errorText;
- set => Set(ref _errorText, value);
- }
-
- private RoamingSettingsHelper _roamingSettings;
-
- private ObservableCollection> _additionalData;
- public ObservableCollection> AdditionalData
- {
- get => _additionalData;
- set => Set(ref _additionalData, value);
- }
-
- private string _keyInputText;
- public string KeyInputText
- {
- get => _keyInputText;
- set => Set(ref _keyInputText, value);
- }
-
- private string _valueInputText;
- public string ValueInputText
- {
- get => _valueInputText;
- set => Set(ref _valueInputText, value);
- }
-
- public RoamingSettingsViewModel()
- {
- _roamingSettings = null;
- _keyInputText = string.Empty;
- _valueInputText = string.Empty;
-
- ProviderManager.Instance.ProviderStateChanged += (s, e) => CheckState();
- CheckState();
- }
-
- public void GetValue()
- {
- try
- {
- ErrorText = string.Empty;
- ValueInputText = string.Empty;
-
- string key = KeyInputText;
- string value = _roamingSettings.Read(key);
-
- ValueInputText = value;
- }
- catch (Exception e)
- {
- ErrorText = e.Message;
- }
- }
-
- public void SetValue()
- {
- try
- {
- ErrorText = string.Empty;
-
- _roamingSettings.Save(KeyInputText, ValueInputText);
-
- SyncRoamingSettings();
- }
- catch (Exception e)
- {
- ErrorText = e.Message;
- }
- }
-
- public async void CreateCustomRoamingSettings()
- {
- try
- {
- ErrorText = string.Empty;
-
- await _roamingSettings.Create();
-
- AdditionalData = new ObservableCollection>(_roamingSettings.Cache);
-
- KeyInputText = string.Empty;
- ValueInputText = string.Empty;
- }
- catch (Exception e)
- {
- ErrorText = e.Message;
- }
- }
-
- public async void DeleteCustomRoamingSettings()
- {
- try
- {
- ErrorText = string.Empty;
-
- await _roamingSettings.Delete();
-
- AdditionalData?.Clear();
- KeyInputText = string.Empty;
- ValueInputText = string.Empty;
- }
- catch (Exception e)
- {
- ErrorText = e.Message;
- }
- }
-
- public async void SyncRoamingSettings()
- {
- try
- {
- ErrorText = string.Empty;
- AdditionalData?.Clear();
-
- await _roamingSettings.Sync();
- if (_roamingSettings.Cache != null)
- {
- AdditionalData = new ObservableCollection>(_roamingSettings.Cache);
- }
- }
- catch (Exception e)
- {
- ErrorText = e.Message;
- }
- }
-
- private async void CheckState()
- {
- if (GlobalProvider != null && GlobalProvider.State == ProviderState.SignedIn)
- {
- await LoadState();
- }
- else
- {
- ClearState();
- }
- }
-
- private async Task LoadState()
- {
- try
- {
- ClearState();
-
- _roamingSettings = await RoamingSettingsHelper.CreateForCurrentUser();
- }
- catch (Exception e)
- {
- ErrorText = e.Message;
- }
- }
-
- private void ClearState()
- {
- _roamingSettings = null;
-
- KeyInputText = string.Empty;
- ValueInputText = string.Empty;
- }
-
- private void Set(ref T field, T value, [CallerMemberName] string propertyName = null)
- {
- if (!EqualityComparer.Default.Equals(field, value))
- {
- field = value;
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
- }
- }
- }
-}
diff --git a/UnitTests/UnitTests.UWP/RoamingSettings/Test_OneDriveDataStore.cs b/UnitTests/UnitTests.UWP/RoamingSettings/Test_OneDriveDataStore.cs
index c3bcf12..432ec27 100644
--- a/UnitTests/UnitTests.UWP/RoamingSettings/Test_OneDriveDataStore.cs
+++ b/UnitTests/UnitTests.UWP/RoamingSettings/Test_OneDriveDataStore.cs
@@ -3,11 +3,13 @@
// See the LICENSE file in the project root for more information.
using CommunityToolkit.Authentication;
-using CommunityToolkit.Graph.Uwp.Helpers.RoamingSettings;
+using CommunityToolkit.Graph.Helpers.RoamingSettings;
+using Microsoft.Toolkit.Helpers;
using Microsoft.Toolkit.Uwp;
using Microsoft.Toolkit.Uwp.Helpers;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
+using System.Linq;
using System.Threading.Tasks;
namespace UnitTests.UWP.Helpers
@@ -28,17 +30,11 @@ void test()
{
try
{
- string userId = "TestUserId";
- string dataStoreId = "RoamingData.json";
- IObjectSerializer serializer = new SystemSerializer();
-
- IRoamingSettingsDataStore dataStore = new OneDriveDataStore(userId, dataStoreId, serializer, false);
-
+ var userId = "TestUserId";
+ var storageHelper = new OneDriveStorageHelper(userId);
+
// Evaluate the default state is as expected
- Assert.IsFalse(dataStore.AutoSync);
- Assert.IsNotNull(dataStore.Cache);
- Assert.AreEqual(dataStoreId, dataStore.Id);
- Assert.AreEqual(userId, dataStore.UserId);
+ Assert.AreEqual(userId, storageHelper.UserId);
tcs.SetResult(true);
}
@@ -53,12 +49,9 @@ void test()
await tcs.Task;
}
- ///
- /// Test the dafault state of a new instance of the OneDriveDataStore.
- ///
[TestCategory("RoamingSettings")]
[TestMethod]
- public async Task Test_Sync()
+ public async Task Test_FileCRUD()
{
var tcs = new TaskCompletionSource();
@@ -66,65 +59,92 @@ async void test()
{
try
{
- string userId = "TestUserId";
- string dataStoreId = "RoamingData.json";
- IObjectSerializer serializer = new SystemSerializer();
+ var filePath = "TestFile.txt";
+ var fileContents = "this is a test";
+ var fileContents2 = "this is also a test";
+ var storageHelper = await OneDriveStorageHelper.CreateForCurrentUserAsync();
- IRoamingSettingsDataStore dataStore = new OneDriveDataStore(userId, dataStoreId, serializer, false);
+ // Create a file
+ await storageHelper.CreateFileAsync(filePath, fileContents);
- try
- {
- // Attempt to delete the remote first.
- await dataStore.Delete();
- }
- catch
- {
- }
+ // Read a file
+ var readContents = await storageHelper.ReadFileAsync(filePath);
+ Assert.AreEqual(fileContents, readContents);
- dataStore.SyncCompleted += async (s, e) =>
- {
- try
- {
- // Create a second instance to ensure that the Cache doesn't yield a false positive.
- IRoamingSettingsDataStore dataStore2 = new OneDriveDataStore(userId, dataStoreId, serializer, false);
- await dataStore2.Sync();
-
- var foo = dataStore.Read("foo");
- Assert.AreEqual("bar", foo);
-
- tcs.SetResult(true);
- }
- catch (Exception ex)
- {
- tcs.SetException(ex);
- }
- };
-
- dataStore.SyncFailed = (s, e) =>
- {
- try
- {
- Assert.Fail("Sync Failed");
- }
- catch (Exception ex)
- {
- tcs.SetException(ex);
- }
- };
-
- dataStore.Save("foo", "bar");
- await dataStore.Sync();
+ // Update a file
+ await storageHelper.CreateFileAsync(filePath, fileContents2);
+ var readContents2 = await storageHelper.ReadFileAsync(filePath);
+ Assert.AreEqual(fileContents2, readContents2);
+
+ // Delete a file
+ await storageHelper.DeleteItemAsync(filePath);
+
+ tcs.SetResult(true);
}
catch (Exception ex)
{
tcs.SetException(ex);
}
- }
+ };
PrepareProvider(test);
- var result = await tcs.Task;
- Assert.IsTrue(result);
+ await tcs.Task;
+ }
+
+ [TestCategory("RoamingSettings")]
+ [TestMethod]
+ public async Task Test_FolderCRUD()
+ {
+ var tcs = new TaskCompletionSource();
+
+ async void test()
+ {
+ try
+ {
+ var subfolderName = "TestSubFolder";
+ var folderName = "TestFolder";
+ var fileName = "TestFile.txt";
+ var filePath = $"{folderName}/{fileName}";
+ var fileContents = "this is a test";
+ var storageHelper = await OneDriveStorageHelper.CreateForCurrentUserAsync();
+
+ // Create a folder
+ await storageHelper.CreateFolderAsync(folderName);
+
+ // Create a subfolder
+ await storageHelper.CreateFolderAsync(subfolderName, folderName);
+
+ // Create a file in a folder
+ await storageHelper.CreateFileAsync(filePath, fileContents);
+
+ // Read a file from a folder
+ var readContents = await storageHelper.ReadFileAsync(filePath);
+ Assert.AreEqual(fileContents, readContents);
+
+ // List folder contents
+ var folderItems = await storageHelper.ReadFolderAsync(folderName);
+ var folderItemsList = folderItems.ToList();
+ Assert.AreEqual(2, folderItemsList.Count());
+ Assert.AreEqual(subfolderName, folderItemsList[0].Name);
+ Assert.AreEqual(DirectoryItemType.Folder, folderItemsList[0].ItemType);
+ Assert.AreEqual(fileName, folderItemsList[1].Name);
+ Assert.AreEqual(DirectoryItemType.File, folderItemsList[1].ItemType);
+
+ // Delete a folder
+ await storageHelper.DeleteItemAsync(folderName);
+
+ tcs.SetResult(true);
+ }
+ catch (Exception ex)
+ {
+ tcs.SetException(ex);
+ }
+ };
+
+ PrepareProvider(test);
+
+ await tcs.Task;
}
///
diff --git a/UnitTests/UnitTests.UWP/RoamingSettings/Test_UserExtensionDataStore.cs b/UnitTests/UnitTests.UWP/RoamingSettings/Test_UserExtensionDataStore.cs
index 9ab65cc..7f9d070 100644
--- a/UnitTests/UnitTests.UWP/RoamingSettings/Test_UserExtensionDataStore.cs
+++ b/UnitTests/UnitTests.UWP/RoamingSettings/Test_UserExtensionDataStore.cs
@@ -3,9 +3,10 @@
// See the LICENSE file in the project root for more information.
using CommunityToolkit.Authentication;
-using CommunityToolkit.Graph.Uwp.Helpers.RoamingSettings;
+using CommunityToolkit.Graph.Helpers.RoamingSettings;
+using Microsoft.Toolkit.Extensions;
+using Microsoft.Toolkit.Helpers;
using Microsoft.Toolkit.Uwp;
-using Microsoft.Toolkit.Uwp.Helpers;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Threading.Tasks;
@@ -29,16 +30,14 @@ void test()
try
{
string userId = "TestUserId";
- string dataStoreId = "RoamingData";
- IObjectSerializer serializer = new SystemSerializer();
+ string extensionId = "RoamingData";
- IRoamingSettingsDataStore dataStore = new UserExtensionDataStore(userId, dataStoreId, serializer, false);
-
- // Evaluate the default state is as expected
- Assert.IsFalse(dataStore.AutoSync);
- Assert.IsNotNull(dataStore.Cache);
- Assert.AreEqual(dataStoreId, dataStore.Id);
- Assert.AreEqual(userId, dataStore.UserId);
+ UserExtensionStorageHelper storageHelper = new UserExtensionStorageHelper(extensionId, userId);
+
+ Assert.AreEqual(extensionId, storageHelper.ExtensionId);
+ Assert.AreEqual(userId, storageHelper.UserId);
+ Assert.IsNotNull(storageHelper.Serializer);
+ Assert.IsInstanceOfType(storageHelper.Serializer, typeof(SystemSerializer));
tcs.SetResult(true);
}
@@ -66,31 +65,23 @@ async void test()
{
try
{
- string userId = "TestUserId";
- string dataStoreId = "RoamingData";
- IObjectSerializer serializer = new SystemSerializer();
+ string extensionId = "RoamingData";
- IRoamingSettingsDataStore dataStore = new UserExtensionDataStore(userId, dataStoreId, serializer, false);
+ string testKey = "foo";
+ string testValue = "bar";
- try
- {
- // Attempt to delete the remote first.
- await dataStore.Delete();
- }
- catch
- {
- }
+ var dataStore = await UserExtensionStorageHelper.CreateForCurrentUserAsync(extensionId);
dataStore.SyncCompleted += async (s, e) =>
{
try
{
// Create a second instance to ensure that the Cache doesn't yield a false positive.
- IRoamingSettingsDataStore dataStore2 = new OneDriveDataStore(userId, dataStoreId, serializer, false);
+ var dataStore2 = await UserExtensionStorageHelper.CreateForCurrentUserAsync(extensionId);
await dataStore2.Sync();
- var foo = dataStore.Read("foo");
- Assert.AreEqual("bar", foo);
+ Assert.IsTrue(dataStore.TryRead(testKey, out string storedValue));
+ Assert.AreEqual(testValue, storedValue);
tcs.SetResult(true);
}
@@ -112,7 +103,8 @@ async void test()
}
};
- dataStore.Save("foo", "bar");
+ dataStore.Clear();
+ dataStore.Save(testKey, testValue);
await dataStore.Sync();
}
catch (Exception ex)
@@ -123,8 +115,7 @@ async void test()
PrepareProvider(test);
- var result = await tcs.Task;
- Assert.IsTrue(result);
+ await tcs.Task;
}
///
diff --git a/UnitTests/UnitTests.UWP/UnitTests.UWP.csproj b/UnitTests/UnitTests.UWP/UnitTests.UWP.csproj
index 4547df5..3e6dccf 100644
--- a/UnitTests/UnitTests.UWP/UnitTests.UWP.csproj
+++ b/UnitTests/UnitTests.UWP/UnitTests.UWP.csproj
@@ -159,12 +159,15 @@
5.10.3
-
- 2.0.0
+
+ 4.0.0
6.2.12
+
+ 7.0.0-build.1396
+
7.0.1
@@ -193,9 +196,9 @@
{2E4A708A-DF53-4863-B797-E14CDC6B90FA}
CommunityToolkit.Authentication.Uwp
-
- {42252EE8-7E68-428F-972B-6D2DD3AA12CC}
- CommunityToolkit.Graph.Uwp
+
+ {B2246169-0CD8-473C-AFF6-172310E2C3F6}
+ CommunityToolkit.Graph
diff --git a/nuget.config b/nuget.config
index 2ebdc9b..0615bd0 100644
--- a/nuget.config
+++ b/nuget.config
@@ -1,4 +1,4 @@
-
+