Skip to content

Instantly share code, notes, and snippets.

@Kikimora
Last active September 23, 2017 14:58
Show Gist options
  • Save Kikimora/57ceac2f3dc681febb82087efc7d57fb to your computer and use it in GitHub Desktop.
Save Kikimora/57ceac2f3dc681febb82087efc7d57fb to your computer and use it in GitHub Desktop.
public enum Network { Bitcoin, Ethereum }
//This interface hides ethereum/bitcoin inconsistencies and provides unified interface to view model.
//This make sense only until ETH/BTC has very similar UI and workflow.
//This interface is private to SendViewModel since it is designed with it specific tasks in mind.
interface ISendRequest
{
decimal AvailableFunds { get; }
string Recipient { get; set; }
decimal Fee { get; set; }
void ValidateRecipeint(Ivalidator validator);
void Send();
}
//This view model holds all data required to send cryptocurrency to some recipient
public class SendViewModel : DataContext, IWeakListener
{
//_sendRequest actually holds all data
//View model delegates most properties to send request and
//User fills it by changing properties of this view model
ISendRequest _sendRequest;
public SendViewModel()
{
SendCommand = new AsyncCommand(Send, _ => !_.IsRunning);
PropertyChanged += ValidateCalculatedProperties;
//JavvyContext holds all global services.
//Subscribe to events which comes not from UI but from our model.
//Subscriptions are implemented as weak references to avoid memory leaks.
JavvyContext.MessageHub.Subscribe("Bitcoin.AvailableFunds", this);
JavvyContext.MessageHub.Subscribe("Ethereum.AvailableFunds", this);
//Init _sendRequest
Network = Network.Bitcoin;
}
//Change AvailableFunds property when we read corresponding msesage from blockhain network
void IWeakListener.OnMessage(Notification notification)
{
RaisePropertyChanged(nameof(AvailableFunds));
}
public decimal Amount
{
get => this._sendRequest.Amount;
set
{
this.Validator.CheckProperty(value).MoreOrEqThan(0);
if (value != this._sendRequest.Amount)
{
this._sendRequest.Amount = value;
RaisePropertyChanged(nameof(Amount));
OnTotalTransactionAmountChanged();
}
}
}
//Only valid for bitcoin
public decimal Fee
{
get => this._sendRequest.Fee;
set
{
//Fee cannot be less than 0
this.Validator.CheckProperty(value).MoreOrEqThan(0).LessThan();
if (value != this._sendRequest.Fee)
{
this._sendRequest.Fee = value;
RaisePropertyChanged(nameof(Fee));
OnTotalTransactionAmountChanged();
}
}
}
public decimal TotalTransactionAmount
{
get
{
return Amount + Fee;
}
}
string _recipient;
public string Recipient
{
get => this._sendRequest.Recipient;
set
{
var checker = this.Validator.CheckProperty(value).NotEmpty();
this._sendRequest.ValidateRecipient(Validator);
if (value != this._sendRequest.Recipient)
{
this._sendRequest.Recipient = value;
RaisePropertyChanged(nameof(Recipient));
}
}
}
Network _network;
public Network Network
{
get => _network;
set
{
if (value != _network)
{
_network = value;
var request = _network == Network.Bitcoin ? new BitcoinSendRequest() : EthereumSendRequest();
//Copy data from model to request to preserve it over Netwrok property changes
//Mapper is an instance of AutoMapper configured to map between SendViewModel and ISendRequest
JavvyContext.Mapper.Map(this, request);
this._sendRequest = request;
RaisePropertyChanged(nameof(Network));
RaisePropertyChanged(nameof(Fee));
OnAvailableFundsChanged();
Validate();
}
}
}
public decimal AvailableFunds
{
get
{
return this._sendRequest.AvailableFunds;
}
}
private void OnTotalTransactionAmountChanged()
{
ValidateTotalAmount();
RaisePropertyChanged(nameof(TotalTransactionAmount));
}
private void ValidateTotalAmount()
{
this.Validator.Check(TotalTransactionAmount, nameof(TotalTransactionAmount))
.LessThan(AvailableFunds);
}
private void OnAvailableFundsChanged()
{
RaisePropertyChanged(nameof(AvailableFunds));
//TotalTransactionAmount depends on AvailableFunds;
OnTotalTransactionAmountChanged();
}
private async Task Send(object arg, CancellationToken token)
{
_sendStrategy.Send(Recipient, Amount, Fee);
}
}
abstract class BaseSendRequest : ISendRequest
{
protected readonly TransactionDto _dto = new TrdansactionDto();
public string Recipient { get => _dto.Recipient; set => _dto.Recipient = value; }
public decimal Amount { get => _dto.Amount; set => _dto.Amount = value; }
public abstract void ValidateRecipient(IValidator validator);
public void Send()
{
JavvyContext.TransactionService.Send(_dto);
}
}
class BitcoinSendRequest : BaseSendRequest
{
public decimal Fee { get => _dto.Fee; set => _dto.Fee = value; }
public decimal AvailableFunds => JavvyContext.User.Bitcoin.AvailableFunds;
public Error ValidateRecipient(Ivalidator validator)
{
this.validator.Check(nameof(Recipient), recipient).NotEmpty().Convert(BitcoinAddress.Parse).NotNull();
}
}
class EthereumSendRequest : BaseSendRequest
{
public decimal Fee { get => 0; set => {} }
public decimal AvailableFunds => JavvyContext.User.Ethereum.AvailableFunds;
public void ValidateRecipient(Check<string> recipient)
{
this.validator.Check(nameof(Recipient), recipient).NotEmpty().Convert(EthereumAddress.Parse).NotNull();
}
}
public interface IWeakListener
{
void OnMessage(Notification notification);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment