From 7020ca031c077943e4a7b1f9a09463b9dc4a2875 Mon Sep 17 00:00:00 2001 From: Etienne Baudoux Date: Sun, 10 Oct 2021 08:52:02 -0700 Subject: [PATCH] Persist settings in each tools (#23) --- .../Api/Core/Settings/SettingDefinition.cs | 7 ++ .../Base64EncoderDecoderToolViewModel.cs | 49 ++++++++++-- .../HashGeneratorToolViewModel.cs | 28 ++++++- .../JsonFormatterToolViewModel.cs | 26 +++++-- .../Tools/JsonYaml/JsonYamlToolViewModel.cs | 74 ++++++++++++------- .../Tools/TextDiff/TextDiffToolViewModel.cs | 21 +++++- .../JsonFormatter/JsonFormatterToolPage.xaml | 3 +- .../Tools/JsonYaml/JsonYamlToolPage.xaml | 4 +- .../Providers/Tools/HashGeneratorTests.cs | 1 + 9 files changed, 161 insertions(+), 52 deletions(-) diff --git a/src/dev/impl/DevToys/Api/Core/Settings/SettingDefinition.cs b/src/dev/impl/DevToys/Api/Core/Settings/SettingDefinition.cs index 6222f5351d..ea7192f921 100644 --- a/src/dev/impl/DevToys/Api/Core/Settings/SettingDefinition.cs +++ b/src/dev/impl/DevToys/Api/Core/Settings/SettingDefinition.cs @@ -33,6 +33,13 @@ namespace DevToys.Api.Core.Settings /// The default value of the setting. public SettingDefinition(string name, bool isRoaming, T defaultValue) { + if (string.IsNullOrEmpty(name) || name.Length > 255) + { + // For both LocalSettings and RoamingSettings, the name of each setting can be 255 characters in length at most. + // see https://docs.microsoft.com/en-us/uwp/api/windows.storage.applicationdata.localsettings?view=winrt-22000#remarks + throw new ArgumentOutOfRangeException(nameof(name)); + } + IsRoaming = isRoaming; Name = name; DefaultValue = defaultValue; diff --git a/src/dev/impl/DevToys/ViewModels/Tools/Base64EncoderDecoder/Base64EncoderDecoderToolViewModel.cs b/src/dev/impl/DevToys/ViewModels/Tools/Base64EncoderDecoder/Base64EncoderDecoderToolViewModel.cs index 92d738d5f0..bb796905f2 100644 --- a/src/dev/impl/DevToys/ViewModels/Tools/Base64EncoderDecoder/Base64EncoderDecoderToolViewModel.cs +++ b/src/dev/impl/DevToys/ViewModels/Tools/Base64EncoderDecoder/Base64EncoderDecoderToolViewModel.cs @@ -1,5 +1,6 @@ #nullable enable +using DevToys.Api.Core.Settings; using DevToys.Api.Tools; using DevToys.Core; using DevToys.Core.Threading; @@ -16,17 +17,35 @@ namespace DevToys.ViewModels.Tools.Base64EncoderDecoder [Export(typeof(Base64EncoderDecoderToolViewModel))] public class Base64EncoderDecoderToolViewModel : ObservableRecipient, IToolViewModel { + /// + /// Whether the tool should encode or decode Base64. + /// + private static readonly SettingDefinition Conversion + = new( + name: $"{nameof(Base64EncoderDecoderToolViewModel)}.{nameof(Conversion)}", + isRoaming: true, + defaultValue: DefaultConversion); + + /// + /// Whether the tool should encode/decode in Unicode or ASCII. + /// + private static readonly SettingDefinition Encoder + = new( + name: $"{nameof(Base64EncoderDecoderToolViewModel)}.{nameof(Encoder)}", + isRoaming: true, + defaultValue: DefaultEncoding); + private const string DefaultEncoding = "UTF-8"; private const string DefaultConversion = "Encode"; internal const string DecodeConversion = "Decode"; + private readonly ISettingsProvider _settingsProvider; + private readonly Queue _conversionQueue = new(); + private string? _inputValue; private string? _outputValue; - private string _encodingMode = DefaultEncoding; - private string _conversionMode = DefaultConversion; private bool _conversionInProgress; private bool _setPropertyInProgress; - private readonly Queue _conversionQueue = new(); public Type View { get; } = typeof(Base64EncoderDecoderToolPage); @@ -60,14 +79,18 @@ internal string? OutputValue /// internal string ConversionMode { - get => _conversionMode; + get => _settingsProvider.GetSetting(Conversion); set { if (!_setPropertyInProgress) { _setPropertyInProgress = true; ThreadHelper.ThrowIfNotOnUIThread(); - SetProperty(ref _conversionMode, value); + if (!string.Equals(_settingsProvider.GetSetting(Conversion), value, StringComparison.Ordinal)) + { + _settingsProvider.SetSetting(Conversion, value); + OnPropertyChanged(); + } InputValue = OutputValue; _setPropertyInProgress = false; } @@ -79,15 +102,25 @@ internal string ConversionMode /// internal string EncodingMode { - get => _encodingMode; + get => _settingsProvider.GetSetting(Encoder); set { ThreadHelper.ThrowIfNotOnUIThread(); - SetProperty(ref _encodingMode, value); - QueueConversionCalculation(); + if (!string.Equals(_settingsProvider.GetSetting(Encoder), value, StringComparison.Ordinal)) + { + _settingsProvider.SetSetting(Encoder, value); + OnPropertyChanged(); + QueueConversionCalculation(); + } } } + [ImportingConstructor] + public Base64EncoderDecoderToolViewModel(ISettingsProvider settingsProvider) + { + _settingsProvider = settingsProvider; + } + private void QueueConversionCalculation() { _conversionQueue.Enqueue(InputValue ?? string.Empty); diff --git a/src/dev/impl/DevToys/ViewModels/Tools/HashGenerator/HashGeneratorToolViewModel.cs b/src/dev/impl/DevToys/ViewModels/Tools/HashGenerator/HashGeneratorToolViewModel.cs index 7a17679cbb..c656d98cea 100644 --- a/src/dev/impl/DevToys/ViewModels/Tools/HashGenerator/HashGeneratorToolViewModel.cs +++ b/src/dev/impl/DevToys/ViewModels/Tools/HashGenerator/HashGeneratorToolViewModel.cs @@ -1,5 +1,6 @@ #nullable enable +using DevToys.Api.Core.Settings; using DevToys.Api.Tools; using DevToys.Core; using DevToys.Core.Threading; @@ -18,10 +19,19 @@ namespace DevToys.ViewModels.Tools.HashGenerator [Export(typeof(HashGeneratorToolViewModel))] public sealed class HashGeneratorToolViewModel : ObservableRecipient, IToolViewModel { + /// + /// Whether the generated hash should be uppercase or lowercase. + /// + private static readonly SettingDefinition Uppercase + = new( + name: $"{nameof(HashGeneratorToolViewModel)}.{nameof(Uppercase)}", + isRoaming: true, + defaultValue: false); + + private readonly ISettingsProvider _settingsProvider; private readonly Queue _hashCalculationQueue = new(); private bool _calculationInProgress; - private bool _isUppercase; private string? _input; private string? _md5; private string? _sha1; @@ -34,11 +44,15 @@ public sealed class HashGeneratorToolViewModel : ObservableRecipient, IToolViewM internal bool IsUppercase { - get => _isUppercase; + get => _settingsProvider.GetSetting(Uppercase); set { - SetProperty(ref _isUppercase, value); - QueueHashCalculation(); + if (_settingsProvider.GetSetting(Uppercase) != value) + { + _settingsProvider.SetSetting(Uppercase, value); + OnPropertyChanged(); + QueueHashCalculation(); + } } } @@ -76,6 +90,12 @@ internal string? SHA512 private set => SetProperty(ref _sha512, value); } + [ImportingConstructor] + public HashGeneratorToolViewModel(ISettingsProvider settingsProvider) + { + _settingsProvider = settingsProvider; + } + private void QueueHashCalculation() { _hashCalculationQueue.Enqueue(Input ?? string.Empty); diff --git a/src/dev/impl/DevToys/ViewModels/Tools/JsonFormatter/JsonFormatterToolViewModel.cs b/src/dev/impl/DevToys/ViewModels/Tools/JsonFormatter/JsonFormatterToolViewModel.cs index 305b9c0f44..841f8ad474 100644 --- a/src/dev/impl/DevToys/ViewModels/Tools/JsonFormatter/JsonFormatterToolViewModel.cs +++ b/src/dev/impl/DevToys/ViewModels/Tools/JsonFormatter/JsonFormatterToolViewModel.cs @@ -20,6 +20,15 @@ namespace DevToys.ViewModels.Tools.JsonFormatter [Export(typeof(JsonFormatterToolViewModel))] public sealed class JsonFormatterToolViewModel : ObservableRecipient, IToolViewModel { + /// + /// The indentation to apply while formatting. + /// + private static readonly SettingDefinition Indentation + = new( + name: $"{nameof(JsonFormatterToolViewModel)}.{nameof(Indentation)}", + isRoaming: true, + defaultValue: TwoSpaceIndentation); + private const string TwoSpaceIndentation = "TwoSpaces"; private const string FourSpaceIndentation = "FourSpaces"; private const string OneTabIndentation = "OneTab"; @@ -30,7 +39,6 @@ public sealed class JsonFormatterToolViewModel : ObservableRecipient, IToolViewM private bool _formattingInProgress; private string? _inputValue; private string? _outputValue; - private string _indentation = TwoSpaceIndentation; public Type View { get; } = typeof(JsonFormatterToolPage); @@ -39,13 +47,17 @@ public sealed class JsonFormatterToolViewModel : ObservableRecipient, IToolViewM /// /// Gets or sets the desired indentation. /// - internal string Indentation + internal string IndentationMode { - get => _indentation; + get => SettingsProvider.GetSetting(Indentation); set { - SetProperty(ref _indentation, value); - QueueFormatting(); + if (!string.Equals(SettingsProvider.GetSetting(Indentation), value, StringComparison.Ordinal)) + { + SettingsProvider.SetSetting(Indentation, value); + OnPropertyChanged(); + QueueFormatting(); + } } } @@ -124,7 +136,7 @@ private bool FormatJson(string input, out string output) using (var stringWriter = new StringWriter(stringBuilder)) using (var jsonTextWriter = new JsonTextWriter(stringWriter)) { - switch (Indentation) + switch (IndentationMode) { case TwoSpaceIndentation: jsonTextWriter.Formatting = Formatting.Indented; @@ -169,7 +181,7 @@ private bool FormatJson(string input, out string output) } catch (Exception ex) //some other exception { - Logger.LogFault("Json formatter", ex, $"Indentation: {Indentation}"); + Logger.LogFault("Json formatter", ex, $"Indentation: {IndentationMode}"); output = ex.Message; return false; } diff --git a/src/dev/impl/DevToys/ViewModels/Tools/JsonYaml/JsonYamlToolViewModel.cs b/src/dev/impl/DevToys/ViewModels/Tools/JsonYaml/JsonYamlToolViewModel.cs index f9081dcfd0..ddb11e7fdb 100644 --- a/src/dev/impl/DevToys/ViewModels/Tools/JsonYaml/JsonYamlToolViewModel.cs +++ b/src/dev/impl/DevToys/ViewModels/Tools/JsonYaml/JsonYamlToolViewModel.cs @@ -23,6 +23,24 @@ namespace DevToys.ViewModels.Tools.JsonYaml [Export(typeof(JsonYamlToolViewModel))] public sealed class JsonYamlToolViewModel : ObservableRecipient, IToolViewModel { + /// + /// Whether the tool should convert JSON to YAML or YAML to JSON. + /// + private static readonly SettingDefinition Conversion + = new( + name: $"{nameof(JsonYamlToolViewModel)}.{nameof(Conversion)}", + isRoaming: true, + defaultValue: JsonToYaml); + + /// + /// The indentation to apply while converting. + /// + private static readonly SettingDefinition Indentation + = new( + name: $"{nameof(JsonYamlToolViewModel)}.{nameof(Indentation)}", + isRoaming: true, + defaultValue: TwoSpaceIndentation); + internal const string JsonToYaml = nameof(JsonToYaml); internal const string YamlToJson = nameof(YamlToJson); private const string TwoSpaceIndentation = "TwoSpaces"; @@ -36,8 +54,6 @@ public sealed class JsonYamlToolViewModel : ObservableRecipient, IToolViewModel private string? _inputValueLanguage; private string? _outputValue; private string? _outputValueLanguage; - private string _indentation = TwoSpaceIndentation; - private string _conversionMode = JsonToYaml; public Type View { get; } = typeof(JsonYamlToolPage); @@ -48,34 +64,38 @@ public sealed class JsonYamlToolViewModel : ObservableRecipient, IToolViewModel /// internal string ConversionMode { - get => _conversionMode; + get => SettingsProvider.GetSetting(Conversion); set { if (!_setPropertyInProgress) { _setPropertyInProgress = true; ThreadHelper.ThrowIfNotOnUIThread(); - SetProperty(ref _conversionMode, value); - - if (string.Equals(value, JsonToYaml)) + if (!string.Equals(SettingsProvider.GetSetting(Conversion), value, StringComparison.Ordinal)) { - if (JsonHelper.IsValidJson(OutputValue)) - { - InputValue = OutputValue; - } + SettingsProvider.SetSetting(Conversion, value); + OnPropertyChanged(); - InputValueLanguage = "json"; - OutputValueLanguage = "yaml"; - } - else - { - if (YamlHelper.IsValidYaml(OutputValue)) + if (string.Equals(value, JsonToYaml)) { - InputValue = OutputValue; + if (JsonHelper.IsValidJson(OutputValue)) + { + InputValue = OutputValue; + } + + InputValueLanguage = "json"; + OutputValueLanguage = "yaml"; } + else + { + if (YamlHelper.IsValidYaml(OutputValue)) + { + InputValue = OutputValue; + } - InputValueLanguage = "yaml"; - OutputValueLanguage = "json"; + InputValueLanguage = "yaml"; + OutputValueLanguage = "json"; + } } _setPropertyInProgress = false; @@ -86,13 +106,17 @@ internal string ConversionMode /// /// Gets or sets the desired indentation. /// - internal string Indentation + internal string IndentationMode { - get => _indentation; + get => SettingsProvider.GetSetting(Indentation); set { - SetProperty(ref _indentation, value); - QueueConversion(); + if (!string.Equals(SettingsProvider.GetSetting(Indentation), value, StringComparison.Ordinal)) + { + SettingsProvider.SetSetting(Indentation, value); + OnPropertyChanged(); + QueueConversion(); + } } } @@ -199,7 +223,7 @@ private bool ConvertJsonToYaml(string input, out string output) if (jsonObject is not null && jsonObject is not string) { int indent = 0; - switch (Indentation) + switch (IndentationMode) { case TwoSpaceIndentation: indent = 2; @@ -263,7 +287,7 @@ private bool ConvertYamlToJson(string input, out string output) using (var stringWriter = new StringWriter(stringBuilder)) using (var jsonTextWriter = new JsonTextWriter(stringWriter)) { - switch (Indentation) + switch (IndentationMode) { case TwoSpaceIndentation: jsonTextWriter.Formatting = Formatting.Indented; diff --git a/src/dev/impl/DevToys/ViewModels/Tools/TextDiff/TextDiffToolViewModel.cs b/src/dev/impl/DevToys/ViewModels/Tools/TextDiff/TextDiffToolViewModel.cs index 06e4302f41..097d2e1f5c 100644 --- a/src/dev/impl/DevToys/ViewModels/Tools/TextDiff/TextDiffToolViewModel.cs +++ b/src/dev/impl/DevToys/ViewModels/Tools/TextDiff/TextDiffToolViewModel.cs @@ -12,7 +12,15 @@ namespace DevToys.ViewModels.Tools.TextDiff [Export(typeof(TextDiffToolViewModel))] public sealed class TextDiffToolViewModel : ObservableRecipient, IToolViewModel { - private bool _inlineMode; + /// + /// Whether the text difference should be displayed inlined or side by side. + /// + private static readonly SettingDefinition Inline + = new( + name: $"{nameof(TextDiffToolViewModel)}.{nameof(Inline)}", + isRoaming: true, + defaultValue: false); + private string? _oldText; private string? _newText; @@ -24,8 +32,15 @@ public sealed class TextDiffToolViewModel : ObservableRecipient, IToolViewModel internal bool InlineMode { - get => _inlineMode; - set => SetProperty(ref _inlineMode, value, broadcast: true); + get => SettingsProvider.GetSetting(Inline); + set + { + if (SettingsProvider.GetSetting(Inline) != value) + { + SettingsProvider.SetSetting(Inline, value); + OnPropertyChanged(); + } + } } internal string? OldText diff --git a/src/dev/impl/DevToys/Views/Tools/JsonFormatter/JsonFormatterToolPage.xaml b/src/dev/impl/DevToys/Views/Tools/JsonFormatter/JsonFormatterToolPage.xaml index b6a66e4454..ae0bc4a9ba 100644 --- a/src/dev/impl/DevToys/Views/Tools/JsonFormatter/JsonFormatterToolPage.xaml +++ b/src/dev/impl/DevToys/Views/Tools/JsonFormatter/JsonFormatterToolPage.xaml @@ -5,7 +5,6 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:controls="using:DevToys.UI.Controls" - xmlns:formattedtextblock="using:DevToys.UI.Controls.FormattedTextBlock" xmlns:toolkit="using:Microsoft.Toolkit.Uwp.UI.Controls" mc:Ignorable="d" NavigationCacheMode="Required"> @@ -34,7 +33,7 @@ + SelectedValue="{x:Bind ViewModel.IndentationMode, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"> diff --git a/src/dev/impl/DevToys/Views/Tools/JsonYaml/JsonYamlToolPage.xaml b/src/dev/impl/DevToys/Views/Tools/JsonYaml/JsonYamlToolPage.xaml index f76937edb1..1d057b7476 100644 --- a/src/dev/impl/DevToys/Views/Tools/JsonYaml/JsonYamlToolPage.xaml +++ b/src/dev/impl/DevToys/Views/Tools/JsonYaml/JsonYamlToolPage.xaml @@ -5,8 +5,6 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:controls="using:DevToys.UI.Controls" - xmlns:formattedtextblock="using:DevToys.UI.Controls.FormattedTextBlock" - xmlns:texteditor="using:DevToys.UI.Controls.TextEditor" xmlns:ex="using:DevToys.UI.Extensions" xmlns:toolkit="using:Microsoft.Toolkit.Uwp.UI.Controls" mc:Ignorable="d" @@ -61,7 +59,7 @@ + SelectedValue="{x:Bind ViewModel.IndentationMode, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"> diff --git a/src/tests/DevToys.Tests/Providers/Tools/HashGeneratorTests.cs b/src/tests/DevToys.Tests/Providers/Tools/HashGeneratorTests.cs index a923bc25db..e088906c76 100644 --- a/src/tests/DevToys.Tests/Providers/Tools/HashGeneratorTests.cs +++ b/src/tests/DevToys.Tests/Providers/Tools/HashGeneratorTests.cs @@ -19,6 +19,7 @@ public async Task LowerCaseHashingAsync(string input, string expectedResult) await ThreadHelper.RunOnUIThreadAsync(() => { + viewModel.IsUppercase = false; viewModel.Input = input; });