Implement ConfigBase to reduce code duplication
MODIFY
WalletWasabi.Backend/Config.cs
WalletWasabi.Backend/Config.cs
@@ -8,6 +8,7 @@ using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;
+using WalletWasabi.Bases;
using WalletWasabi.Helpers;
using WalletWasabi.Interfaces;
using WalletWasabi.JsonConverters;
@@ -16,11 +17,8 @@ using WalletWasabi.Logging;
namespace WalletWasabi.Backend
{
[JsonObject(MemberSerialization.OptIn)]
- public class Config : IConfig
+ public class Config : ConfigBase
{
- /// <inheritdoc />
- public string FilePath { get; private set; }
-
[JsonProperty(PropertyName = "Network")]
[JsonConverter(typeof(NetworkJsonConverter))]
public Network Network { get; private set; } = Network.Main;
@@ -93,13 +91,12 @@ namespace WalletWasabi.Backend
}
}
- public Config()
+ public Config() : base()
{
}
- public Config(string filePath)
+ public Config(string filePath) : base(filePath)
{
- SetFilePath(filePath);
}
public Config(Network network,
@@ -110,6 +107,7 @@ namespace WalletWasabi.Backend
EndPoint mainNetBitcoinCoreRpcEndPoint,
EndPoint testNetBitcoinCoreRpcEndPoint,
EndPoint regTestBitcoinCoreRpcEndPoint)
+ : base()
{
Network = Guard.NotNull(nameof(network), network);
BitcoinRpcConnectionString = Guard.NotNullOrEmptyOrWhitespace(nameof(bitcoinRpcConnectionString), bitcoinRpcConnectionString);
@@ -123,71 +121,7 @@ namespace WalletWasabi.Backend
RegTestBitcoinCoreRpcEndPoint = Guard.NotNull(nameof(regTestBitcoinCoreRpcEndPoint), regTestBitcoinCoreRpcEndPoint);
}
- /// <inheritdoc />
- public async Task ToFileAsync()
- {
- AssertFilePathSet();
-
- string jsonString = JsonConvert.SerializeObject(this, Formatting.Indented);
- await File.WriteAllTextAsync(FilePath, jsonString, Encoding.UTF8);
- }
-
- /// <inheritdoc />
- public async Task LoadOrCreateDefaultFileAsync()
- {
- AssertFilePathSet();
- JsonConvert.PopulateObject("{}", this);
-
- if (!File.Exists(FilePath))
- {
- Logger.LogInfo<Config>($"{nameof(Config)} file did not exist. Created at path: `{FilePath}`.");
- }
- else
- {
- string jsonString = await File.ReadAllTextAsync(FilePath, Encoding.UTF8);
- JsonConvert.PopulateObject(jsonString, this);
-
- if (TryEnsureBackwardsCompatibility(jsonString))
- {
- await ToFileAsync();
- }
- }
-
- await ToFileAsync();
- }
-
- /// <inheritdoc />
- public async Task<bool> CheckFileChangeAsync()
- {
- AssertFilePathSet();
-
- if (!File.Exists(FilePath))
- {
- throw new FileNotFoundException($"{nameof(Config)} file did not exist at path: `{FilePath}`.");
- }
-
- string jsonString = await File.ReadAllTextAsync(FilePath, Encoding.UTF8);
- var newConfig = JsonConvert.DeserializeObject<JObject>(jsonString);
- var currentConfig = JObject.FromObject(this);
- return !JToken.DeepEquals(newConfig, currentConfig);
- }
-
- /// <inheritdoc />
- public void SetFilePath(string path)
- {
- FilePath = Guard.NotNullOrEmptyOrWhitespace(nameof(path), path, trim: true);
- }
-
- /// <inheritdoc />
- public void AssertFilePathSet()
- {
- if (FilePath is null)
- {
- throw new NotSupportedException($"{nameof(FilePath)} is not set. Use {nameof(SetFilePath)} to set it.");
- }
- }
-
- private bool TryEnsureBackwardsCompatibility(string jsonString)
+ protected override bool TryEnsureBackwardsCompatibility(string jsonString)
{
try
{

MODIFY
WalletWasabi.Gui/Config.cs
WalletWasabi.Gui/Config.cs
@@ -7,6 +7,7 @@ using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;
+using WalletWasabi.Bases;
using WalletWasabi.Gui.Models;
using WalletWasabi.Helpers;
using WalletWasabi.Interfaces;
@@ -18,7 +19,7 @@ using WalletWasabi.TorSocks5;
namespace WalletWasabi.Gui
{
[JsonObject(MemberSerialization.OptIn)]
- public class Config : IConfig
+ public class Config : ConfigBase
{
public const int DefaultPrivacyLevelSome = 2;
public const int DefaultPrivacyLevelFine = 21;
@@ -27,9 +28,6 @@ namespace WalletWasabi.Gui
public const int DefaultTorSock5Port = 9050;
public static readonly Money DefaultDustThreshold = Money.Coins(0.0001m);
- /// <inheritdoc />
- public string FilePath { get; private set; }
-
[JsonProperty(PropertyName = "Network")]
[JsonConverter(typeof(NetworkJsonConverter))]
public Network Network { get; internal set; } = Network.Main;
@@ -150,7 +148,7 @@ namespace WalletWasabi.Gui
[JsonConverter(typeof(MoneyBtcJsonConverter))]
public Money DustThreshold { get; internal set; } = DefaultDustThreshold;
- private Uri _backendUri;
+ private Uri _backendUri = null;
private Uri _fallbackBackendUri;
public ServiceConfiguration ServiceConfiguration { get; private set; }
@@ -231,49 +229,23 @@ namespace WalletWasabi.Gui
}
}
- public Config()
+ public Config() : base()
{
- _backendUri = null;
}
- public Config(string filePath)
+ public Config(string filePath) : base(filePath)
{
- _backendUri = null;
- SetFilePath(filePath);
- }
-
- /// <inheritdoc />
- public async Task ToFileAsync()
- {
- AssertFilePathSet();
-
- string jsonString = JsonConvert.SerializeObject(this, Formatting.Indented);
- await File.WriteAllTextAsync(FilePath,
- jsonString,
- Encoding.UTF8);
}
/// <inheritdoc />
- public async Task LoadOrCreateDefaultFileAsync()
+ public override async Task LoadFileAsync()
{
- AssertFilePathSet();
- JsonConvert.PopulateObject("{}", this);
-
- if (!File.Exists(FilePath))
- {
- Logger.LogInfo<Config>($"{nameof(Config)} file did not exist. Created at path: `{FilePath}`.");
- }
- else
- {
- await LoadFileAsync();
- }
+ await base.LoadFileAsync();
ServiceConfiguration = new ServiceConfiguration(MixUntilAnonymitySet, PrivacyLevelSome, PrivacyLevelFine, PrivacyLevelStrong, GetBitcoinP2pEndPoint(), DustThreshold);
// Just debug convenience.
_backendUri = GetCurrentBackendUri();
-
- await ToFileAsync();
}
public void SetP2PEndpoint(EndPoint endPoint)
@@ -315,24 +287,6 @@ namespace WalletWasabi.Gui
}
}
- public async Task LoadFileAsync()
- {
- string jsonString = await File.ReadAllTextAsync(FilePath, Encoding.UTF8);
-
- var config = JsonConvert.DeserializeObject<Config>(jsonString);
- JsonConvert.PopulateObject(jsonString, this);
-
- ServiceConfiguration = config?.ServiceConfiguration ?? ServiceConfiguration;
-
- // Just debug convenience.
- _backendUri = GetCurrentBackendUri();
-
- if (TryEnsureBackwardsCompatibility(jsonString))
- {
- await ToFileAsync();
- }
- }
-
public static async Task<Config> LoadOrCreateDefaultFileAsync(string path)
{
var config = new Config(path);
@@ -340,50 +294,6 @@ namespace WalletWasabi.Gui
return config;
}
- public void CopyFrom(Config config)
- {
- var configSerialized = JsonConvert.SerializeObject(config);
- JsonConvert.PopulateObject(configSerialized, this);
- }
-
- /// <inheritdoc />
- public async Task<bool> CheckFileChangeAsync()
- {
- AssertFilePathSet();
-
- if (!File.Exists(FilePath))
- {
- throw new FileNotFoundException($"{nameof(Config)} file did not exist at path: `{FilePath}`.");
- }
-
- string jsonString = await File.ReadAllTextAsync(FilePath, Encoding.UTF8);
- var newConfig = JsonConvert.DeserializeObject<Config>(jsonString);
-
- return !AreDeepEqual(newConfig);
- }
-
- public bool AreDeepEqual(Config otherConfig)
- {
- var currentConfig = JObject.FromObject(this);
- var otherConfigJson = JObject.FromObject(otherConfig);
- return JToken.DeepEquals(otherConfigJson, currentConfig);
- }
-
- /// <inheritdoc />
- public void SetFilePath(string path)
- {
- FilePath = Guard.NotNullOrEmptyOrWhitespace(nameof(path), path, trim: true);
- }
-
- /// <inheritdoc />
- public void AssertFilePathSet()
- {
- if (FilePath is null)
- {
- throw new NotSupportedException($"{nameof(FilePath)} is not set. Use {nameof(SetFilePath)} to set it.");
- }
- }
-
public TargetPrivacy GetTargetPrivacy()
{
if (MixUntilAnonymitySet == PrivacyLevelSome)
@@ -443,7 +353,7 @@ namespace WalletWasabi.Gui
return 0;
}
- private bool TryEnsureBackwardsCompatibility(string jsonString)
+ protected override bool TryEnsureBackwardsCompatibility(string jsonString)
{
try
{

MODIFY
WalletWasabi.Gui/MainWindow.xaml.cs
WalletWasabi.Gui/MainWindow.xaml.cs
@@ -8,11 +8,14 @@ using AvalonStudio.Extensibility.Theme;
using AvalonStudio.Shell;
using AvalonStudio.Shell.Controls;
using NBitcoin;
+using ReactiveUI;
using System;
using System.ComponentModel;
using System.Composition;
using System.IO;
using System.Linq;
+using System.Reactive.Linq;
+using System.Reactive.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
@@ -44,13 +47,46 @@ namespace WalletWasabi.Gui
}
}
- public Global Global => (DataContext as MainWindowViewModel)?.Global;
+ public Global Global => MainWindowViewModel.Instance.Global;
private void InitializeComponent()
{
- Activated += OnActivated;
Closing += MainWindow_ClosingAsync;
AvaloniaXamlLoader.Load(this);
+ DisplayWalletManager();
+
+ var uiConfigFilePath = Path.Combine(Global.DataDir, "UiConfig.json");
+ var uiConfig = new UiConfig(uiConfigFilePath);
+ uiConfig.LoadOrCreateDefaultFileAsync()
+ .ToObservable(RxApp.TaskpoolScheduler)
+ .Take(1)
+ .ObserveOn(RxApp.MainThreadScheduler)
+ .Subscribe(_ =>
+ {
+ try
+ {
+ Global.InitializeUiConfig(uiConfig);
+ Application.Current.Resources.AddOrReplace(Global.UiConfigResourceKey, Global.UiConfig);
+ Logging.Logger.LogInfo<UiConfig>("UiConfig is successfully initialized.");
+
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ MainWindowViewModel.Instance.Width = (double)uiConfig.Width;
+ MainWindowViewModel.Instance.Height = (double)uiConfig.Height;
+ MainWindowViewModel.Instance.WindowState = (WindowState)uiConfig.WindowState;
+ }
+ else
+ {
+ MainWindowViewModel.Instance.WindowState = WindowState.Maximized;
+ }
+
+ MainWindowViewModel.Instance.LockScreen.Initialize();
+ }
+ catch (Exception ex)
+ {
+ Logging.Logger.LogError<MainWindowViewModel>(ex);
+ }
+ }, onError: ex => Logging.Logger.LogError<MainWindowViewModel>(ex));
}
protected override void OnDataContextEndUpdate()
@@ -165,43 +201,6 @@ namespace WalletWasabi.Gui
}
}
-#pragma warning disable IDE1006 // Naming Styles
-
- private async void OnActivated(object sender, EventArgs e)
-#pragma warning restore IDE1006 // Naming Styles
- {
- try
- {
- Activated -= OnActivated;
-
- var uiConfigFilePath = Path.Combine(Global.DataDir, "UiConfig.json");
- var uiConfig = new UiConfig(uiConfigFilePath);
- await uiConfig.LoadOrCreateDefaultFileAsync();
- Global.InitializeUiConfig(uiConfig);
- Application.Current.Resources.AddOrReplace(Global.UiConfigResourceKey, Global.UiConfig);
- Logging.Logger.LogInfo<UiConfig>("UiConfig is successfully initialized.");
-
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
- {
- MainWindowViewModel.Instance.Width = (double)uiConfig.Width;
- MainWindowViewModel.Instance.Height = (double)uiConfig.Height;
- MainWindowViewModel.Instance.WindowState = (WindowState)uiConfig.WindowState;
- }
- else
- {
- MainWindowViewModel.Instance.WindowState = WindowState.Maximized;
- }
-
- MainWindowViewModel.Instance.LockScreen.Initialize();
-
- DisplayWalletManager();
- }
- catch (Exception ex)
- {
- Logging.Logger.LogWarning<MainWindow>(ex);
- }
- }
-
private void DisplayWalletManager()
{
var walletManagerViewModel = IoC.Get<WalletManagerViewModel>();

MODIFY
WalletWasabi.Gui/Tabs/SettingsViewModel.cs
WalletWasabi.Gui/Tabs/SettingsViewModel.cs
@@ -136,6 +136,7 @@ namespace WalletWasabi.Gui.Tabs
Config.LoadOrCreateDefaultFileAsync(Global.Config.FilePath)
.ToObservable(RxApp.TaskpoolScheduler)
+ .Take(1)
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(x =>
{

MODIFY
WalletWasabi.Gui/UiConfig.cs
WalletWasabi.Gui/UiConfig.cs
@@ -9,6 +9,7 @@ using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
+using WalletWasabi.Bases;
using WalletWasabi.Gui.Converters;
using WalletWasabi.Gui.Models;
using WalletWasabi.Helpers;
@@ -18,15 +19,12 @@ using WalletWasabi.JsonConverters;
namespace WalletWasabi.Gui
{
[JsonObject(MemberSerialization.OptIn)]
- public class UiConfig : ReactiveObject, IConfig
+ public class UiConfig : ConfigBase
{
private bool _lurkingWifeMode;
private bool _lockScreenActive;
private string _lockScreenPinHash;
- /// <inheritdoc />
- public string FilePath { get; private set; }
-
[JsonProperty(PropertyName = "WindowState")]
[JsonConverter(typeof(WindowStateAfterStartJsonConverter))]
public WindowState WindowState { get; internal set; } = Avalonia.Controls.WindowState.Maximized;
@@ -56,7 +54,7 @@ namespace WalletWasabi.Gui
public bool LurkingWifeMode
{
get => _lurkingWifeMode;
- set => this.RaiseAndSetIfChanged(ref _lurkingWifeMode, value);
+ set => RaiseAndSetIfChanged(ref _lurkingWifeMode, value);
}
[DefaultValue(false)]
@@ -64,7 +62,7 @@ namespace WalletWasabi.Gui
public bool LockScreenActive
{
get => _lockScreenActive;
- set => this.RaiseAndSetIfChanged(ref _lockScreenActive, value);
+ set => RaiseAndSetIfChanged(ref _lockScreenActive, value);
}
[DefaultValue("")]
@@ -72,91 +70,15 @@ namespace WalletWasabi.Gui
public string LockScreenPinHash
{
get => _lockScreenPinHash;
- set => this.RaiseAndSetIfChanged(ref _lockScreenPinHash, value);
- }
-
- public UiConfig()
- {
- }
-
- public UiConfig(string filePath)
- {
- SetFilePath(filePath);
+ set => RaiseAndSetIfChanged(ref _lockScreenPinHash, value);
}
- /// <inheritdoc />
- public async Task ToFileAsync()
+ public UiConfig() : base()
{
- AssertFilePathSet();
-
- string jsonString = JsonConvert.SerializeObject(this, Formatting.Indented);
- await File.WriteAllTextAsync(FilePath,
- jsonString,
- Encoding.UTF8);
}
- /// <inheritdoc />
- public async Task LoadOrCreateDefaultFileAsync()
+ public UiConfig(string filePath) : base(filePath)
{
- AssertFilePathSet();
-
- JsonConvert.PopulateObject("{}", this);
-
- if (!File.Exists(FilePath))
- {
- Logging.Logger.LogInfo<Config>($"{nameof(Config)} file did not exist. Created at path: `{FilePath}`.");
- }
- else
- {
- await LoadFileAsync();
- }
-
- await ToFileAsync();
- }
-
- public async Task LoadFileAsync()
- {
- string jsonString = await File.ReadAllTextAsync(FilePath, Encoding.UTF8);
- var config = JsonConvert.DeserializeObject<UiConfig>(jsonString);
- JsonConvert.PopulateObject(jsonString, this);
- }
-
- /// <inheritdoc />
- public async Task<bool> CheckFileChangeAsync()
- {
- AssertFilePathSet();
-
- if (!File.Exists(FilePath))
- {
- throw new FileNotFoundException($"{nameof(UiConfig)} file did not exist at path: `{FilePath}`.");
- }
-
- string jsonString = await File.ReadAllTextAsync(FilePath, Encoding.UTF8);
- var newConfig = JsonConvert.DeserializeObject<UiConfig>(jsonString);
-
- return !AreDeepEqual(newConfig);
- }
-
- private bool AreDeepEqual(UiConfig otherConfig)
- {
- var currentConfig = JObject.FromObject(this);
- var otherConfigJson = JObject.FromObject(otherConfig);
- return JToken.DeepEquals(otherConfigJson, currentConfig);
- }
-
- /// <inheritdoc />
- public void SetFilePath(string path)
- {
- FilePath = Guard.NotNullOrEmptyOrWhitespace(nameof(path), path, trim: true);
- }
-
- /// <inheritdoc />
- public void AssertFilePathSet()
- {
- if (FilePath is null)
- {
- throw new NotSupportedException($"{nameof(FilePath)} is not set. Use {nameof(SetFilePath)} to set it.");
- }
}
}
}

MODIFY
WalletWasabi.Gui/ViewModels/TextResourceViewModelBase.cs
WalletWasabi.Gui/ViewModels/TextResourceViewModelBase.cs
@@ -50,6 +50,7 @@ namespace WalletWasabi.Gui.ViewModels
Disposables = new CompositeDisposable();
LoadDocumentAsync(Target)
.ToObservable(RxApp.TaskpoolScheduler)
+ .Take(1)
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(x => Text = x)
.DisposeWith(Disposables);

ADD
WalletWasabi/Bases/ConfigBase.cs
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Threading.Tasks;
+using WalletWasabi.Helpers;
+using WalletWasabi.Interfaces;
+using WalletWasabi.Logging;
+
+namespace WalletWasabi.Bases
+{
+ public abstract class ConfigBase : NotifyPropertyChangedBase, IConfig
+ {
+ /// <inheritdoc />
+ public string FilePath { get; private set; } = null;
+
+ public ConfigBase()
+ {
+ }
+
+ public ConfigBase(string filePath)
+ {
+ SetFilePath(filePath);
+ }
+
+ /// <inheritdoc />
+ public void AssertFilePathSet()
+ {
+ if (FilePath is null)
+ {
+ throw new NotSupportedException($"{nameof(FilePath)} is not set. Use {nameof(SetFilePath)} to set it.");
+ }
+ }
+
+ /// <inheritdoc />
+ public async Task<bool> CheckFileChangeAsync()
+ {
+ AssertFilePathSet();
+
+ if (!File.Exists(FilePath))
+ {
+ throw new FileNotFoundException($"{GetType().Name} file did not exist at path: `{FilePath}`.");
+ }
+
+ string jsonString = await File.ReadAllTextAsync(FilePath, Encoding.UTF8);
+
+ var newConfigObject = Activator.CreateInstance(GetType());
+ JsonConvert.PopulateObject(jsonString, newConfigObject);
+
+ return !AreDeepEqual(newConfigObject);
+ }
+
+ /// <inheritdoc />
+ public virtual async Task LoadOrCreateDefaultFileAsync()
+ {
+ AssertFilePathSet();
+ JsonConvert.PopulateObject("{}", this);
+
+ if (!File.Exists(FilePath))
+ {
+ Logger.LogInfo<ConfigBase>($"{GetType().Name} file did not exist. Created at path: `{FilePath}`.");
+ }
+ else
+ {
+ await LoadFileAsync();
+ }
+
+ await ToFileAsync();
+ }
+
+ /// <inheritdoc />
+ public virtual async Task LoadFileAsync()
+ {
+ var jsonString = await File.ReadAllTextAsync(FilePath, Encoding.UTF8);
+
+ JsonConvert.PopulateObject(jsonString, this);
+
+ if (TryEnsureBackwardsCompatibility(jsonString))
+ {
+ await ToFileAsync();
+ }
+ }
+
+ /// <inheritdoc />
+ public void SetFilePath(string path)
+ {
+ FilePath = Guard.NotNullOrEmptyOrWhitespace(nameof(path), path, trim: true);
+ }
+
+ /// <inheritdoc />
+ public bool AreDeepEqual(object otherConfig)
+ {
+ var currentConfig = JObject.FromObject(this);
+ var otherConfigJson = JObject.FromObject(otherConfig);
+ return JToken.DeepEquals(otherConfigJson, currentConfig);
+ }
+
+ /// <inheritdoc />
+ public async Task ToFileAsync()
+ {
+ AssertFilePathSet();
+
+ string jsonString = JsonConvert.SerializeObject(this, Formatting.Indented);
+ await File.WriteAllTextAsync(FilePath, jsonString, Encoding.UTF8);
+ }
+
+ protected virtual bool TryEnsureBackwardsCompatibility(string jsonString) => true;
+ }
+}

ADD
WalletWasabi/Bases/NotifyPropertyChangedBase.cs
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+using System.Text;
+
+namespace WalletWasabi.Bases
+{
+ public class NotifyPropertyChangedBase : INotifyPropertyChanged
+ {
+ #region Events
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+
+ protected bool RaiseAndSetIfChanged<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
+ {
+ if (EqualityComparer<T>.Default.Equals(field, value))
+ {
+ return false;
+ }
+
+ field = value;
+ OnPropertyChanged(propertyName);
+ return true;
+ }
+
+ #endregion Events
+ }
+}

MODIFY
WalletWasabi/Interfaces/IConfig.cs
WalletWasabi/Interfaces/IConfig.cs
using System.Threading.Tasks;
+using WalletWasabi.Bases;
namespace WalletWasabi.Interfaces
{
@@ -29,6 +30,13 @@ namespace WalletWasabi.Interfaces
/// </summary>
Task LoadOrCreateDefaultFileAsync();
+ /// <summary>
+ /// Load config if the file path of the config file is set, otherwise throw exception.
+ /// </summary>
+ Task LoadFileAsync();
+
+ bool AreDeepEqual(object otherConfig);
+
/// <summary>
/// Check if the config file differs from the config if the file path of the config file is set, otherwise throw exception.
/// </summary>

MODIFY
WalletWasabi/Models/ChaumianCoinJoin/CcjRoundConfig.cs
WalletWasabi/Models/ChaumianCoinJoin/CcjRoundConfig.cs
@@ -6,6 +6,7 @@ using System.ComponentModel;
using System.IO;
using System.Text;
using System.Threading.Tasks;
+using WalletWasabi.Bases;
using WalletWasabi.Helpers;
using WalletWasabi.Interfaces;
using WalletWasabi.JsonConverters;
@@ -14,11 +15,8 @@ using WalletWasabi.Logging;
namespace WalletWasabi.Models.ChaumianCoinJoin
{
[JsonObject(MemberSerialization.OptIn)]
- public class CcjRoundConfig : IConfig
+ public class CcjRoundConfig : ConfigBase
{
- /// <inheritdoc />
- public string FilePath { get; internal set; }
-
[JsonProperty(PropertyName = "Denomination")]
[JsonConverter(typeof(MoneyBtcJsonConverter))]
public Money Denomination { get; internal set; } = Money.Coins(0.1m);
@@ -71,46 +69,12 @@ namespace WalletWasabi.Models.ChaumianCoinJoin
[JsonProperty(PropertyName = "MaximumMixingLevelCount", DefaultValueHandling = DefaultValueHandling.Populate)]
public int MaximumMixingLevelCount { get; internal set; }
- public CcjRoundConfig()
+ public CcjRoundConfig() : base()
{
}
- public CcjRoundConfig(string filePath)
+ public CcjRoundConfig(string filePath) : base(filePath)
{
- SetFilePath(filePath);
- }
-
- /// <inheritdoc />
- public async Task ToFileAsync()
- {
- AssertFilePathSet();
-
- string jsonString = JsonConvert.SerializeObject(this, Formatting.Indented);
- await File.WriteAllTextAsync(FilePath,
- jsonString,
- Encoding.UTF8);
- }
-
- /// <inheritdoc />
- public async Task LoadOrCreateDefaultFileAsync()
- {
- AssertFilePathSet();
-
- JsonConvert.PopulateObject("{}", this);
-
- if (!File.Exists(FilePath))
- {
- Logger.LogInfo<CcjRoundConfig>($"{nameof(CcjRoundConfig)} file did not exist. Created at path: `{FilePath}`.");
- }
- else
- {
- string jsonString = await File.ReadAllTextAsync(FilePath, Encoding.UTF8);
- var config = JsonConvert.DeserializeObject<CcjRoundConfig>(jsonString);
-
- await UpdateOrDefaultAsync(config, toFile: false);
- }
-
- await ToFileAsync();
}
public async Task UpdateOrDefaultAsync(CcjRoundConfig config, bool toFile)
@@ -124,37 +88,5 @@ namespace WalletWasabi.Models.ChaumianCoinJoin
await ToFileAsync();
}
}
-
- /// <inheritdoc />
- public async Task<bool> CheckFileChangeAsync()
- {
- AssertFilePathSet();
-
- if (!File.Exists(FilePath))
- {
- throw new FileNotFoundException($"{nameof(CcjRoundConfig)} file did not exist at path: `{FilePath}`.");
- }
-
- string jsonString = await File.ReadAllTextAsync(FilePath, Encoding.UTF8);
- var newConfig = JsonConvert.DeserializeObject<JObject>(jsonString);
- var currentConfig = JObject.FromObject(this);
-
- return !JToken.DeepEquals(newConfig, currentConfig);
- }
-
- /// <inheritdoc />
- public void SetFilePath(string path)
- {
- FilePath = Guard.NotNullOrEmptyOrWhitespace(nameof(path), path, trim: true);
- }
-
- /// <inheritdoc />
- public void AssertFilePathSet()
- {
- if (FilePath is null)
- {
- throw new NotSupportedException($"{nameof(FilePath)} is not set. Use {nameof(SetFilePath)} to set it.");
- }
- }
}
}

MODIFY
WalletWasabi/Models/SmartCoin.cs
WalletWasabi/Models/SmartCoin.cs
using NBitcoin;
using Newtonsoft.Json;
using System;
+using System.Collections.Generic;
using System.ComponentModel;
+using System.Runtime.CompilerServices;
+using WalletWasabi.Bases;
using WalletWasabi.Helpers;
using WalletWasabi.JsonConverters;
using WalletWasabi.KeyManagement;
@@ -12,19 +15,8 @@ namespace WalletWasabi.Models
/// An UTXO that knows more.
/// </summary>
[JsonObject(MemberSerialization.OptIn)]
- public class SmartCoin : IEquatable<SmartCoin>, INotifyPropertyChanged
+ public class SmartCoin : NotifyPropertyChangedBase, IEquatable<SmartCoin>
{
- #region Events
-
- public event PropertyChangedEventHandler PropertyChanged;
-
- private void OnPropertyChanged(string propertyName)
- {
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
- }
-
- #endregion Events
-
#region Fields
private uint256 _transactionId;
@@ -61,28 +53,14 @@ namespace WalletWasabi.Models
public uint256 TransactionId
{
get => _transactionId;
- set
- {
- if (value != _transactionId)
- {
- _transactionId = value;
- OnPropertyChanged(nameof(TransactionId));
- }
- }
+ set => RaiseAndSetIfChanged(ref _transactionId, value);
}
[JsonProperty]
public uint Index
{
get => _index;
- set
- {
- if (value != _index)
- {
- _index = value;
- OnPropertyChanged(nameof(Index));
- }
- }
+ set => RaiseAndSetIfChanged(ref _index, value);
}
[JsonProperty]
@@ -90,14 +68,7 @@ namespace WalletWasabi.Models
public Script ScriptPubKey
{
get => _scriptPubKey;
- set
- {
- if (value != _scriptPubKey)
- {
- _scriptPubKey = value;
- OnPropertyChanged(nameof(ScriptPubKey));
- }
- }
+ set => RaiseAndSetIfChanged(ref _scriptPubKey, value);
}
[JsonProperty]
@@ -105,14 +76,7 @@ namespace WalletWasabi.Models
public Money Amount
{
get => _amount;
- set
- {
- if (value != _amount)
- {
- _amount = value;
- OnPropertyChanged(nameof(Amount));
- }
- }
+ set => RaiseAndSetIfChanged(ref _amount, value);
}
[JsonProperty]
@@ -154,14 +118,7 @@ namespace WalletWasabi.Models
public TxoRef[] SpentOutputs
{
get => _spentOutputs;
- set
- {
- if (value != _spentOutputs)
- {
- _spentOutputs = value;
- OnPropertyChanged(nameof(SpentOutputs));
- }
- }
+ set => RaiseAndSetIfChanged(ref _spentOutputs, value);
}
[JsonProperty]
@@ -169,28 +126,14 @@ namespace WalletWasabi.Models
public bool IsReplaceable
{
get => _replaceable && !Confirmed;
- set
- {
- if (value != _replaceable)
- {
- _replaceable = value;
- OnPropertyChanged(nameof(IsReplaceable));
- }
- }
+ set => RaiseAndSetIfChanged(ref _replaceable, value);
}
[JsonProperty]
public int AnonymitySet
{
get => _anonymitySet;
- private set
- {
- if (value != _anonymitySet)
- {
- _anonymitySet = value;
- OnPropertyChanged(nameof(AnonymitySet));
- }
- }
+ private set => RaiseAndSetIfChanged(ref _anonymitySet, value);
}
[JsonProperty]
@@ -266,14 +209,7 @@ namespace WalletWasabi.Models
public HdPubKey HdPubKey
{
get => _hdPubKey;
- private set
- {
- if (value != _hdPubKey)
- {
- _hdPubKey = value;
- OnPropertyChanged(nameof(HdPubKey));
- }
- }
+ private set => RaiseAndSetIfChanged(ref _hdPubKey, value);
}
#endregion SerializableProperties
@@ -287,27 +223,13 @@ namespace WalletWasabi.Models
public ISecret Secret
{
get => _secret;
- set
- {
- if (value != _secret)
- {
- _secret = value;
- OnPropertyChanged(nameof(Secret));
- }
- }
+ set => RaiseAndSetIfChanged(ref _secret, value);
}
public string Clusters
{
get => _clusters;
- private set
- {
- if (value != _clusters)
- {
- _clusters = value;
- OnPropertyChanged(nameof(Clusters));
- }
- }
+ private set => RaiseAndSetIfChanged(ref _clusters, value);
}
#endregion NonSerializableProperties
@@ -317,14 +239,7 @@ namespace WalletWasabi.Models
public bool Confirmed
{
get => _confirmed;
- private set
- {
- if (value != _confirmed)
- {
- _confirmed = value;
- OnPropertyChanged(nameof(Confirmed));
- }
- }
+ private set => RaiseAndSetIfChanged(ref _confirmed, value);
}
/// <summary>
@@ -333,14 +248,7 @@ namespace WalletWasabi.Models
public bool Unavailable
{
get => _unavailable;
- private set
- {
- if (value != _unavailable)
- {
- _unavailable = value;
- OnPropertyChanged(nameof(Unavailable));
- }
- }
+ private set => RaiseAndSetIfChanged(ref _unavailable, value);
}
public bool Unspent
@@ -361,14 +269,7 @@ namespace WalletWasabi.Models
public bool IsBanned
{
get => _isBanned;
- private set
- {
- if (value != _isBanned)
- {
- _isBanned = value;
- OnPropertyChanged(nameof(IsBanned));
- }
- }
+ private set => RaiseAndSetIfChanged(ref _isBanned, value);
}
#endregion DependentProperties

MODIFY
WalletWasabi/Services/WasabiSynchronizer.cs
WalletWasabi/Services/WasabiSynchronizer.cs
@@ -13,6 +13,7 @@ using System.Threading;
using System.Threading.Tasks;
using WalletWasabi.Backend.Models;
using WalletWasabi.Backend.Models.Responses;
+using WalletWasabi.Bases;
using WalletWasabi.Exceptions;
using WalletWasabi.Helpers;
using WalletWasabi.Logging;
@@ -22,7 +23,7 @@ using WalletWasabi.WebClients.Wasabi;
namespace WalletWasabi.Services
{
- public class WasabiSynchronizer : INotifyPropertyChanged
+ public class WasabiSynchronizer : NotifyPropertyChangedBase
{
#region MembersPropertiesEvents
@@ -40,15 +41,7 @@ namespace WalletWasabi.Services
public decimal UsdExchangeRate
{
get => _usdExchangeRate;
-
- private set
- {
- if (_usdExchangeRate != value)
- {
- _usdExchangeRate = value;
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(UsdExchangeRate)));
- }
- }
+ private set => RaiseAndSetIfChanged(ref _usdExchangeRate, value);
}
private AllFeeEstimate _allFeeEstimate;
@@ -56,15 +49,7 @@ namespace WalletWasabi.Services
public AllFeeEstimate AllFeeEstimate
{
get => _allFeeEstimate;
-
- private set
- {
- if (_allFeeEstimate != value)
- {
- _allFeeEstimate = value;
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(AllFeeEstimate)));
- }
- }
+ private set => RaiseAndSetIfChanged(ref _allFeeEstimate, value);
}
private TorStatus _torStatus;
@@ -72,15 +57,7 @@ namespace WalletWasabi.Services
public TorStatus TorStatus
{
get => _torStatus;
-
- private set
- {
- if (_torStatus != value)
- {
- _torStatus = value;
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(TorStatus)));
- }
- }
+ private set => RaiseAndSetIfChanged(ref _torStatus, value);
}
private BackendStatus _backendStatus;
@@ -88,15 +65,7 @@ namespace WalletWasabi.Services
public BackendStatus BackendStatus
{
get => _backendStatus;
-
- private set
- {
- if (_backendStatus != value)
- {
- _backendStatus = value;
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(BackendStatus)));
- }
- }
+ private set => RaiseAndSetIfChanged(ref _backendStatus, value);
}
public TimeSpan MaxRequestIntervalForMixing { get; set; }
@@ -110,8 +79,6 @@ namespace WalletWasabi.Services
public BitcoinStore BitcoinStore { get; private set; }
- public event PropertyChangedEventHandler PropertyChanged;
-
public event EventHandler<bool> ResponseArrivedIsGenSocksServFail;
public event EventHandler<SynchronizeResponse> ResponseArrived;

MODIFY
WalletWasabi/Stores/HashChain.cs
WalletWasabi/Stores/HashChain.cs
@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
+using WalletWasabi.Bases;
using WalletWasabi.Helpers;
using WalletWasabi.Models;
@@ -12,7 +13,7 @@ namespace WalletWasabi.Stores
/// <summary>
/// High performance chain index and cache.
/// </summary>
- public class HashChain : INotifyPropertyChanged
+ public class HashChain : NotifyPropertyChangedBase
{
private int _tipHeight;
private uint256 _tipHash;
@@ -23,71 +24,34 @@ namespace WalletWasabi.Stores
private SortedDictionary<int, uint256> Chain { get; }
private object Lock { get; }
- public event PropertyChangedEventHandler PropertyChanged;
-
public int TipHeight
{
get => _tipHeight;
- private set
- {
- if (_tipHeight != value)
- {
- _tipHeight = value;
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(TipHeight)));
- }
- }
+ private set => RaiseAndSetIfChanged(ref _tipHeight, value);
}
public uint256 TipHash
{
get => _tipHash;
- private set
- {
- if (_tipHash != value)
- {
- _tipHash = value;
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(TipHash)));
- }
- }
+ private set => RaiseAndSetIfChanged(ref _tipHash, value);
}
public int ServerTipHeight
{
get => _serverTipHeight;
- private set
- {
- if (_serverTipHeight != value)
- {
- _serverTipHeight = value;
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ServerTipHeight)));
- }
- }
+ private set => RaiseAndSetIfChanged(ref _serverTipHeight, value);
}
public int HashesLeft
{
get => _hashesLeft;
- private set
- {
- if (_hashesLeft != value)
- {
- _hashesLeft = value;
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(HashesLeft)));
- }
- }
+ private set => RaiseAndSetIfChanged(ref _hashesLeft, value);
}
public int HashCount
{
get => _hashesCount;
- private set
- {
- if (_hashesCount != value)
- {
- _hashesCount = value;
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(HashCount)));
- }
- }
+ private set => RaiseAndSetIfChanged(ref _hashesCount, value);
}
public HashChain()

Implement ConfigBase to reduce code duplication