Skip to content

Commit

Permalink
Fix invoke in main thread issue for macOS, increase version to 1.2.3
Browse files Browse the repository at this point in the history
  • Loading branch information
matsakiv committed Feb 17, 2022
1 parent 79a7e7e commit 7193852
Show file tree
Hide file tree
Showing 27 changed files with 174 additions and 126 deletions.
4 changes: 2 additions & 2 deletions Atomex.Client.Desktop.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
<UseAppHost>true</UseAppHost>
<ApplicationIcon>Resources/Images/logo_dark_256x256.ico</ApplicationIcon>

<AssemblyVersion>1.2.2</AssemblyVersion>
<Version>1.2.2</Version>
<AssemblyVersion>1.2.3</AssemblyVersion>
<Version>1.2.3</Version>
</PropertyGroup>
<ItemGroup>
<AvaloniaResource Include="Assets\**" />
Expand Down
32 changes: 32 additions & 0 deletions Common/ObservableExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,44 @@
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reactive.Concurrency;
using System.Reactive.Linq;

using ReactiveUI;
using ReactiveUI.Fody.Helpers;

namespace Atomex.Client.Desktop.Common
{
public static class ObservableExtensions
{
public static IObservable<(T1, T2)> WhereAllNotNull<T1, T2>(this IObservable<(T1?, T2?)> observable) =>
observable.Where(t => t.Item1 != null && t.Item2 != null);

public static IDisposable SubscribeInMainThread<T>(
this IObservable<T> observable,
Action<T> onNext)
{
return observable
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(onNext);
}

public static ObservableAsPropertyHelper<TRet> ToPropertyExInMainThread<TObj, TRet>(
this IObservable<TRet> item,
TObj source,
Expression<Func<TObj, TRet>> property,
bool deferSubscription = false) where TObj : ReactiveObject
{
return item.ToPropertyEx(source, property, deferSubscription, RxApp.MainThreadScheduler);
}

public static IDisposable InvokeCommandInMainThread<T, TResult>(
this IObservable<T> item,
ReactiveCommandBase<T, TResult>? command)
{
return item
.ObserveOn(RxApp.MainThreadScheduler)
.InvokeCommand(command);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,18 +67,18 @@ public ConversionCurrencyViewModel()

this.WhenAnyValue(vm => vm.CurrencyViewModel)
.Select(i => i != null)
.ToPropertyEx(this, vm => vm.Selected);
.ToPropertyExInMainThread(this, vm => vm.Selected);

this.WhenAnyValue(vm => vm.CurrencyViewModel)
.Select(vm => vm?.CurrencyFormat ?? "0")
.ToPropertyEx(this, vm => vm.CurrencyFormat);
.ToPropertyExInMainThread(this, vm => vm.CurrencyFormat);

this.WhenAnyValue(vm => vm.Amount,
vm => vm.CurrencyFormat,
(amount, currencyFormat) => {
return amount.ToString(currencyFormat, CultureInfo.CurrentCulture);
})
.ToPropertyEx(this, vm => vm.AmountString);
.ToPropertyExInMainThread(this, vm => vm.AmountString);
}

public void RaiseGotInputFocus()
Expand Down
68 changes: 31 additions & 37 deletions ViewModels/ConversionViewModels/ConversionViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ public ConversionViewModel(IAtomexApp app)
// FromCurrencyViewModel changed => Update ToCurrencies
this.WhenAnyValue(vm => vm.FromViewModel.CurrencyViewModel)
.WhereNotNull()
.Subscribe(c =>
.SubscribeInMainThread(c =>
{
ToCurrencies = FromCurrencies
?.Where(fc => Symbols.SymbolByCurrencies(fc.Currency, c.Currency) != null)
Expand All @@ -205,7 +205,7 @@ public ConversionViewModel(IAtomexApp app)

// ToCurrencies list changed => check & update ToViewModel and ToCurrencyViewModelItem
this.WhenAnyValue(vm => vm.ToCurrencies)
.Subscribe(c =>
.SubscribeInMainThread(c =>
{
if (ToViewModel.CurrencyViewModel == null)
return;
Expand All @@ -223,7 +223,7 @@ public ConversionViewModel(IAtomexApp app)
});

this.WhenAnyValue(vm => vm.ToCurrencyViewModelItem)
.Subscribe(i =>
.SubscribeInMainThread(i =>
{
// if To currency not selected or To currency is Bitcoin based
if (i == null || Atomex.Currencies.IsBitcoinBased(i.CurrencyViewModel.Currency.Name))
Expand All @@ -244,7 +244,7 @@ public ConversionViewModel(IAtomexApp app)
vm => vm.FromViewModel.CurrencyViewModel,
vm => vm.ToViewModel.CurrencyViewModel)
.WhereAllNotNull()
.Subscribe(t =>
.SubscribeInMainThread(t =>
{
var symbol = Symbols.SymbolByCurrencies(t.Item1.Currency, t.Item2.Currency);

Expand All @@ -271,32 +271,32 @@ public ConversionViewModel(IAtomexApp app)

// From Amount changed => update FromViewModel.AmountInBase
this.WhenAnyValue(vm => vm.FromViewModel.AmountString)
.Subscribe(amount => UpdateFromAmountInBase());
.SubscribeInMainThread(amount => UpdateFromAmountInBase());

// To Amount changed => update ToViewModel.AmountInBase
this.WhenAnyValue(vm => vm.ToViewModel.AmountString)
.Subscribe(amount => UpdateToAmountInBase());
.SubscribeInMainThread(amount => UpdateToAmountInBase());

// EstimatedPaymentFee changed => update EstimatedPaymentFeeInBase
this.WhenAnyValue(vm => vm.EstimatedPaymentFee)
.Subscribe(amount => UpdateEstimatedPaymentFeeInBase());
.SubscribeInMainThread(amount => UpdateEstimatedPaymentFeeInBase());

// EstimatedRedeemFee changed => update EstimatedRedeemFeeInBase
this.WhenAnyValue(vm => vm.EstimatedRedeemFee)
.Subscribe(amount => UpdateEstimatedRedeemFeeInBase());
.SubscribeInMainThread(amount => UpdateEstimatedRedeemFeeInBase());

// RewardForRedeem changed => update RewardForRedeemInBase
this.WhenAnyValue(vm => vm.RewardForRedeem)
.Subscribe(amount => UpdateRewardForRedeemInBase());
.SubscribeInMainThread(amount => UpdateRewardForRedeemInBase());

// RewardForRedeem changed => update HasRewardForRedeem
this.WhenAnyValue(vm => vm.RewardForRedeem)
.Select(r => r > 0)
.ToPropertyEx(this, vm => vm.HasRewardForRedeem);
.ToPropertyExInMainThread(this, vm => vm.HasRewardForRedeem);

// EstimatedMakerNetworkFee changed => update EstimatedMakerNetworkFeeInBase
this.WhenAnyValue(vm => vm.EstimatedMakerNetworkFee)
.Subscribe(amount => UpdateEstimatedMakerNetworkFeeInBase());
.SubscribeInMainThread(amount => UpdateEstimatedMakerNetworkFeeInBase());

// If fees in base currency changed => update TotalNetworkFeeInBase
this.WhenAnyValue(
Expand All @@ -306,19 +306,18 @@ public ConversionViewModel(IAtomexApp app)
vm => vm.EstimatedMakerNetworkFeeInBase,
vm => vm.RewardForRedeemInBase)
.Throttle(TimeSpan.FromMilliseconds(1))
.Subscribe(t => UpdateTotalNetworkFeeInBase());
.SubscribeInMainThread(t => UpdateTotalNetworkFeeInBase());

// AmountInBase or EstimatedTotalNetworkFeeInBase changed => check the ratio of the fee to the amount
this.WhenAnyValue(
vm => vm.FromViewModel.AmountInBase,
vm => vm.EstimatedTotalNetworkFeeInBase)
.Subscribe(t => CheckAmountToFeeRatio());
.SubscribeInMainThread(t => CheckAmountToFeeRatio());

this.WhenAnyValue(
vm => vm.IsInsufficientFunds,
vm => vm.IsNoLiquidity)
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(t =>
.SubscribeInMainThread(t =>
{
FromViewModel.IsAmountValid = !IsInsufficientFunds && !IsNoLiquidity;
ToViewModel.IsAmountValid = !IsInsufficientFunds && !IsNoLiquidity;
Expand All @@ -333,8 +332,7 @@ public ConversionViewModel(IAtomexApp app)
vm => vm.ToViewModel.IsAmountValid,
vm => vm.FromViewModel.AmountInBase)
.Throttle(TimeSpan.FromMilliseconds(1))
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(t =>
.SubscribeInMainThread(t =>
{
var estimatedTotalNetworkFeeInBase = EstimatedTotalNetworkFeeInBase;
var amountInBase = FromViewModel.AmountInBase;
Expand All @@ -354,35 +352,34 @@ public ConversionViewModel(IAtomexApp app)

this.WhenAnyValue(vm => vm.AmountValidationMessageType)
.Select(t => t == MessageType.Warning)
.ToPropertyEx(this, vm => vm.IsAmountValidationWarning);
.ToPropertyExInMainThread(this, vm => vm.IsAmountValidationWarning);

this.WhenAnyValue(vm => vm.AmountValidationMessageType)
.Select(t => t == MessageType.Error)
.ToPropertyEx(this, vm => vm.IsAmountValidationError);
.ToPropertyExInMainThread(this, vm => vm.IsAmountValidationError);

this.WhenAnyValue(vm => vm.MessageType)
.Select(t => t == MessageType.Warning)
.ToPropertyEx(this, vm => vm.IsWarning);
.ToPropertyExInMainThread(this, vm => vm.IsWarning);

this.WhenAnyValue(vm => vm.MessageType)
.Select(t => t == MessageType.Error)
.ToPropertyEx(this, vm => vm.IsError);
.ToPropertyExInMainThread(this, vm => vm.IsError);

this.WhenAnyValue(vm => vm.DGSelectedIndex)
.Select(i => i != -1)
.ToPropertyEx(this, vm => vm.DetailsVisible);
.ToPropertyExInMainThread(this, vm => vm.DetailsVisible);

this.WhenAnyValue(vm => vm.DetailsVisible)
.Select(dv => dv ? 1 : 2)
.ToPropertyEx(this, vm => vm.ColumnSpan);
.ToPropertyExInMainThread(this, vm => vm.ColumnSpan);

this.WhenAnyValue(
vm => vm.DGSelectedIndex,
vm => vm.Swaps,
(selectedSwapIndex, _) => selectedSwapIndex
)
(selectedSwapIndex, _) => selectedSwapIndex)
.Select(selectedSwapIndex => selectedSwapIndex != -1 ? Swaps?[selectedSwapIndex]?.Details : null)
.ToPropertyEx(this, vm => vm.SwapDetailsViewModel);
.ToPropertyExInMainThread(this, vm => vm.SwapDetailsViewModel);

SubscribeToServices();
}
Expand Down Expand Up @@ -781,8 +778,6 @@ private void CheckAmountToFeeRatio()
{
Message = string.Empty;
}

//CanExchange = amountInBase == 0 || estimatedTotalNetworkFeeInBase / amountInBase <= 0.75m;
}

protected async void OnBaseQuotesUpdatedEventHandler(object? sender, EventArgs args)
Expand Down Expand Up @@ -1021,35 +1016,34 @@ private void DesignerMode()
{
this.WhenAnyValue(vm => vm.AmountValidationMessageType)
.Select(t => t == MessageType.Warning)
.ToPropertyEx(this, vm => vm.IsAmountValidationWarning);
.ToPropertyExInMainThread(this, vm => vm.IsAmountValidationWarning);

this.WhenAnyValue(vm => vm.AmountValidationMessageType)
.Select(t => t == MessageType.Error)
.ToPropertyEx(this, vm => vm.IsAmountValidationError);
.ToPropertyExInMainThread(this, vm => vm.IsAmountValidationError);

this.WhenAnyValue(vm => vm.MessageType)
.Select(t => t == MessageType.Warning)
.ToPropertyEx(this, vm => vm.IsWarning);
.ToPropertyExInMainThread(this, vm => vm.IsWarning);

this.WhenAnyValue(vm => vm.MessageType)
.Select(t => t == MessageType.Error)
.ToPropertyEx(this, vm => vm.IsError);
.ToPropertyExInMainThread(this, vm => vm.IsError);

this.WhenAnyValue(vm => vm.DGSelectedIndex)
.Select(i => i != -1)
.ToPropertyEx(this, vm => vm.DetailsVisible);
.ToPropertyExInMainThread(this, vm => vm.DetailsVisible);

this.WhenAnyValue(vm => vm.DetailsVisible)
.Select(dv => dv ? 1 : 2)
.ToPropertyEx(this, vm => vm.ColumnSpan);
.ToPropertyExInMainThread(this, vm => vm.ColumnSpan);

this.WhenAnyValue(
vm => vm.DetailsVisible,
vm => vm.Swaps,
(detailsVisible, _) => detailsVisible
)
(detailsVisible, _) => detailsVisible)
.Select(detailsVisible => detailsVisible ? Swaps?[DGSelectedIndex]?.Details : null)
.ToPropertyEx(this, vm => vm.SwapDetailsViewModel);
.ToPropertyExInMainThread(this, vm => vm.SwapDetailsViewModel);

var btc = DesignTime.Currencies.Get<BitcoinConfig>("BTC");
var ltc = DesignTime.Currencies.Get<LitecoinConfig>("LTC");
Expand Down
8 changes: 4 additions & 4 deletions ViewModels/ConversionViewModels/SelectCurrencyViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public SelectCurrencyWithOutputsViewModelItem(

return $"from {outputs.Count()} outputs ({totalAmountString} {currency.Name})";
})
.ToPropertyEx(this, vm => vm.SelectedAddressDescription);
.ToPropertyExInMainThread(this, vm => vm.SelectedAddressDescription);

AvailableOutputs = availableOutputs ?? throw new ArgumentNullException(nameof(availableOutputs));
SelectedOutputs = selectedOutputs ?? availableOutputs;
Expand Down Expand Up @@ -132,7 +132,7 @@ public SelectCurrencyWithAddressViewModelItem(

return $"{prefix} {address.Address.TruncateAddress()} ({balanceString} {CurrencyViewModel.Currency.Name})";
})
.ToPropertyEx(this, vm => vm.SelectedAddressDescription);
.ToPropertyExInMainThread(this, vm => vm.SelectedAddressDescription);

AvailableAddresses = availableAddresses ?? throw new ArgumentNullException(nameof(availableAddresses));
SelectedAddress = selectedAddress ?? availableAddresses.MaxByOrDefault(w => w.Balance);
Expand Down Expand Up @@ -245,7 +245,7 @@ public SelectCurrencyViewModel(

this.WhenAnyValue(vm => vm.SelectedCurrency)
.WhereNotNull()
.Subscribe(i => {
.SubscribeInMainThread(i => {
CurrencySelected?.Invoke(i);
});
}
Expand All @@ -258,7 +258,7 @@ public SelectCurrencyViewModel()

this.WhenAnyValue(vm => vm.SelectedCurrency)
.WhereNotNull()
.Subscribe(i => {
.SubscribeInMainThread(i => {
CurrencySelected?.Invoke(i);
});
}
Expand Down
2 changes: 1 addition & 1 deletion ViewModels/CurrencyViewModels/CurrencyViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ protected CurrencyViewModel(CurrencyConfig currency)

this.WhenAnyValue(vm => vm.UnconfirmedAmount)
.Select(ua => ua != 0)
.ToPropertyEx(this, vm => vm.HasUnconfirmedAmount);
.ToPropertyExInMainThread(this, vm => vm.HasUnconfirmedAmount);
}

protected virtual async Task UpdateAsync()
Expand Down
4 changes: 2 additions & 2 deletions ViewModels/ReceiveViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ public ReceiveViewModel(
this.WhenAnyValue(vm => vm.SelectedAddress)
.WhereNotNull()
.Select(_ => Unit.Default)
.InvokeCommand(createQrCodeCommand);
.InvokeCommandInMainThread(createQrCodeCommand);

this.WhenAnyValue(vm => vm.SelectedAddress)
.Subscribe(_ => IsCopied = false);
.SubscribeInMainThread(_ => IsCopied = false);

TokenContract = tokenContract;
TokenType = tokenType;
Expand Down
6 changes: 3 additions & 3 deletions ViewModels/SendViewModels/BitcoinBasedSendViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,11 @@ public BitcoinBasedSendViewModel(
: outputs.ElementAt(0)
.DestinationAddress(Config.Network)
.TruncateAddress())
.ToPropertyEx(this, vm => vm.FromBeautified);
.ToPropertyExInMainThread(this, vm => vm.FromBeautified);

this.WhenAnyValue(vm => vm.Outputs)
.WhereNotNull()
.Subscribe(outputs =>
.SubscribeInMainThread(outputs =>
{
From = outputs.Count != 1
? $"{outputs.Count} outputs"
Expand Down Expand Up @@ -135,7 +135,7 @@ protected override void FromClick()

protected override void ToClick()
{
SelectToViewModel.BackAction = () => Desktop.App.DialogService.Show(this);
SelectToViewModel.BackAction = () => App.DialogService.Show(this);

App.DialogService.Show(SelectToViewModel);
}
Expand Down
8 changes: 6 additions & 2 deletions ViewModels/SendViewModels/Erc20SendViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Threading.Tasks;

using Avalonia.Controls;
using Avalonia.Threading;
using Serilog;

using Atomex.Blockchain.Abstract;
Expand Down Expand Up @@ -161,8 +162,11 @@ protected override void OnQuotesUpdatedEventHandler(object sender, EventArgs arg
var quote = quotesProvider.GetQuote(CurrencyCode, BaseCurrencyCode);
var ethQuote = quotesProvider.GetQuote(Currency.FeeCurrencyName, BaseCurrencyCode);

AmountInBase = Amount.SafeMultiply(quote?.Bid ?? 0m);
FeeInBase = FeeAmount.SafeMultiply(ethQuote?.Bid ?? 0m);
Dispatcher.UIThread.InvokeAsync(() =>
{
AmountInBase = Amount.SafeMultiply(quote?.Bid ?? 0m);
FeeInBase = FeeAmount.SafeMultiply(ethQuote?.Bid ?? 0m);
});
}

protected override Task<Error> Send(CancellationToken cancellationToken = default)
Expand Down
Loading

0 comments on commit 7193852

Please sign in to comment.