[Feature] Extend Input/Output Lists to TxPreview and TxDetails (#13507)
This commit is contained in:
Родитель
39aabb4825
Коммит
04667a8584
|
@ -6,4 +6,10 @@ public static class IntConverter
|
|||
{
|
||||
public static readonly IValueConverter ToOrdinalString =
|
||||
new FuncValueConverter<int, string>(x => $"{x}.");
|
||||
|
||||
public static readonly IValueConverter IsNullOrZero =
|
||||
new FuncValueConverter<int?, bool>(x => x is null or 0);
|
||||
|
||||
public static readonly IValueConverter FPlusConverter =
|
||||
new FuncValueConverter<int, string>(x => x > 0 ? "+" + x : x.ToString());
|
||||
}
|
||||
|
|
|
@ -115,6 +115,7 @@
|
|||
<StreamGeometry x:Key="stats_wallet_regular">m 21.192982 12.984447 c 0.997413 0 1.805901 0.808529 1.805901 1.805902 v 6.390112 c 0 0.997377 -0.808488 1.805902 -1.805901 1.805902 h -7.501436 c -0.997373 0 -1.805902 -0.808525 -1.805902 -1.805902 v -6.390112 c 0 -0.997373 0.808529 -1.805902 1.805902 -1.805902 z m 0 0.833493 h -7.501436 c -0.537048 0 -0.972409 0.435361 -0.972409 0.972409 v 6.390112 c 0 0.537048 0.435361 0.972408 0.972409 0.972408 h 7.501436 c 0.537047 0 0.972409 -0.43536 0.972409 -0.972408 v -6.390112 c 0 -0.537048 -0.435362 -0.972409 -0.972409 -0.972409 z m -6.66886 1.111324 h 5.837756 c 0.230165 0 0.416747 0.186582 0.416747 0.416746 0 0.210985 -0.15678 0.385343 -0.360199 0.41294 l -0.05654 0.0038 H 14.52413 c -0.230165 0 -0.416746 -0.186583 -0.416746 -0.416746 0 -0.210985 0.156789 -0.385343 0.360198 -0.412941 l 0.05654 -0.0038 h 5.837756 z M 20.25 3.00001 c 0.4142 0 0.75 0.33579 0.75 0.75 v 4.99996 c 0 0.41422 -0.3358 0.75001 -0.75 0.75001 -0.4142 0 -0.75 -0.33579 -0.75 -0.75 V 5.56066 l -5.9697 5.96964 c -0.2929 0.2929 -0.7677 0.2929 -1.0606 0 L 10.25 9.31066 4.28033 15.2803 c -0.29289 0.2929 -0.76777 0.2929 -1.06066 0 -0.29289 -0.2929 -0.29289 -0.7677 0 -1.0606 l 6.5 -6.50003 c 0.29293 -0.29289 0.76773 -0.29289 1.06063 0 L 13 9.93934 18.4394 4.50001 15.25 4.5 C 14.8358 4.5 14.5 4.16421 14.5 3.75 14.5 3.33578 14.8358 3 15.25 3 Z</StreamGeometry>
|
||||
<StreamGeometry x:Key="navigation_regular">M2.75254822,18 L21.2525482,18 C21.6667618,18 22.0025482,18.3357864 22.0025482,18.75 C22.0025482,19.1296958 21.7203943,19.443491 21.3543188,19.4931534 L21.2525482,19.5 L2.75254822,19.5 C2.33833466,19.5 2.00254822,19.1642136 2.00254822,18.75 C2.00254822,18.3703042 2.2847021,18.056509 2.65077766,18.0068466 L2.75254822,18 L21.2525482,18 L2.75254822,18 Z M2.75254822,11.5030063 L21.2525482,11.5030063 C21.6667618,11.5030063 22.0025482,11.8387927 22.0025482,12.2530063 C22.0025482,12.6327021 21.7203943,12.9464972 21.3543188,12.9961597 L21.2525482,13.0030063 L2.75254822,13.0030063 C2.33833466,13.0030063 2.00254822,12.6672199 2.00254822,12.2530063 C2.00254822,11.8733105 2.2847021,11.5595153 2.65077766,11.5098529 L2.75254822,11.5030063 L21.2525482,11.5030063 L2.75254822,11.5030063 Z M2.75168905,5.0032392 L21.251689,5.0032392 C21.6659026,5.0032392 22.001689,5.33902564 22.001689,5.7532392 C22.001689,6.13293497 21.7195352,6.44673016 21.3534596,6.49639258 L21.251689,6.5032392 L2.75168905,6.5032392 C2.33747549,6.5032392 2.00168905,6.16745276 2.00168905,5.7532392 C2.00168905,5.37354343 2.28384293,5.05974824 2.64991849,5.01008582 L2.75168905,5.0032392 L21.251689,5.0032392 L2.75168905,5.0032392 Z</StreamGeometry>
|
||||
<StreamGeometry x:Key="arrow_left_regular">M12.7347,4.20949 C13.0332,3.92233 13.508,3.93153 13.7952,4.23005 C14.0823,4.52857 14.0731,5.00335 13.7746,5.29051 L5.50039,13.25 L24.2532,13.25 C24.6674,13.25 25.0032,13.5858 25.0032,13.9999982 C25.0032,14.4142 24.6674,14.75 24.2532,14.75 L5.50137,14.75 L13.7746,22.7085 C14.0731,22.9957 14.0823,23.4705 13.7952,23.769 C13.508,24.0675 13.0332,24.0767 12.7347,23.7896 L3.30673,14.7202 C2.89776,14.3268 2.89776,13.6723 3.30673,13.2788 L12.7347,4.20949 Z</StreamGeometry>
|
||||
<StreamGeometry x:Key="arrow_right_regular">M1.5 6a.5.5 0 0 1 .5-.5h6.793L6.146 2.854a.5.5 0 1 1 .708-.708l3.5 3.5a.5.5 0 0 1 0 .708l-3.5 3.5a.5.5 0 0 1-.708-.708L8.793 6.5H2a.5.5 0 0 1-.5-.5</StreamGeometry>
|
||||
<StreamGeometry x:Key="add_regular">M8.41687 7.57953V2.41851C8.41687 2.18743 8.22932 1.99988 7.99823 1.99988C7.76715 1.99988 7.5796 2.18743 7.5796 2.41851V7.57953H2.41863C2.18755 7.57953 2 7.76708 2 7.99816C2 8.22925 2.18755 8.41679 2.41863 8.41679H7.5796V13.5812C7.5796 13.8123 7.76715 13.9999 7.99823 13.9999C8.22932 13.9999 8.41687 13.8123 8.41687 13.5812V8.41679L13.5799 8.41851C13.811 8.41851 13.9985 8.23096 13.9985 7.99988C13.9985 7.76879 13.811 7.58125 13.5799 7.58125L8.41687 7.57953Z</StreamGeometry>
|
||||
<StreamGeometry x:Key="recover_arrow_right_regular">M3 6.42589C3 3.98154 4.98154 2 7.42589 2H11.4092C11.6536 2 11.8518 2.19815 11.8518 2.44259C11.8518 2.68702 11.6536 2.88518 11.4092 2.88518H7.42589C5.47041 2.88518 3.88518 4.47041 3.88518 6.42589C3.88518 8.38137 5.47041 9.9666 7.42589 9.9666H12.1111L9.72185 7.57734C9.54901 7.4045 9.54901 7.12427 9.72185 6.95143C9.89469 6.77859 10.1749 6.77859 10.3478 6.95143L13.4804 10.0841C13.5995 10.2032 13.6365 10.3733 13.5915 10.5241C13.5741 10.6042 13.5342 10.6804 13.472 10.7427L10.3443 13.8704C10.1715 14.0432 9.89123 14.0432 9.71839 13.8704C9.54555 13.6975 9.54555 13.4173 9.71839 13.2445L12.1111 10.8518H7.42589C4.98154 10.8518 3 8.87024 3 6.42589Z</StreamGeometry>
|
||||
<StreamGeometry x:Key="import_regular">M3.5 13H12.5C12.7761 13 13 13.2239 13 13.5C13 13.7455 12.8231 13.9496 12.5899 13.9919L12.5 14H3.5C3.22386 14 3 13.7761 3 13.5C3 13.2545 3.17688 13.0504 3.41012 13.0081L3.5 13H12.5H3.5ZM7.91012 1.00806L8 1C8.24546 1 8.44961 1.17688 8.49194 1.41012L8.5 1.5V10.292L11.182 7.61091C11.3555 7.43735 11.625 7.41806 11.8198 7.55306L11.8891 7.61091C12.0627 7.78448 12.0819 8.0539 11.9469 8.24877L11.8891 8.31802L8.35355 11.8536C8.17999 12.0271 7.91056 12.0464 7.71569 11.9114L7.64645 11.8536L4.11091 8.31802C3.91565 8.12276 3.91565 7.80617 4.11091 7.61091C4.28448 7.43735 4.5539 7.41806 4.74877 7.55306L4.81802 7.61091L7.5 10.292V1.5C7.5 1.25454 7.67688 1.05039 7.91012 1.00806L8 1L7.91012 1.00806Z</StreamGeometry>
|
||||
|
|
|
@ -40,8 +40,8 @@ public partial class TransactionModel : ReactiveObject
|
|||
public Lazy<IReadOnlyCollection<OutPoint>> ForeignInputs => new(ForeignInputsFunction());
|
||||
public required IReadOnlyCollection<SmartCoin> WalletInputs { get; set; }
|
||||
|
||||
public required Func<IReadOnlyCollection<OutPoint>> ForeignOutputsFunction{ get; set; }
|
||||
public Lazy<IReadOnlyCollection<OutPoint>> ForeignOutputs => new(ForeignOutputsFunction());
|
||||
public required Func<IReadOnlyCollection<IndexedTxOut>> ForeignOutputsFunction{ get; set; }
|
||||
public Lazy<IReadOnlyCollection<IndexedTxOut>> ForeignOutputs => new(ForeignOutputsFunction());
|
||||
public required IReadOnlyCollection<SmartCoin> WalletOutputs { get; set; }
|
||||
public required Money Amount { get; set; }
|
||||
|
||||
|
|
|
@ -13,8 +13,8 @@ public static class CoinjoinCoinListDataGridSource
|
|||
public static HierarchicalTreeDataGridSource<CoinjoinCoinListItem> Create(IEnumerable<CoinjoinCoinListItem> source)
|
||||
{
|
||||
// [Column] [View] [Header] [Width] [MinWidth] [MaxWidth] [CanUserSort]
|
||||
// AnonymityScore AnonymityColumnView <custom> 50 - - true
|
||||
// Amount AmountColumnView Amount Auto - - true
|
||||
// AnonymityScore AnonymityColumnView <custom> 3 Star - - true
|
||||
// Amount AmountColumnView Amount 8 Star - - true
|
||||
var result = new HierarchicalTreeDataGridSource<CoinjoinCoinListItem>(source)
|
||||
{
|
||||
Columns =
|
||||
|
|
|
@ -36,8 +36,16 @@ public partial class CoinJoinsDetailsViewModel : RoutableViewModel
|
|||
var freshWalletInputs = allWalletInputs.Where(x => !allWalletOutputs.Select(y => y.Outpoint).Contains(x.Outpoint)).OrderByDescending(x => x.Amount).ToList();
|
||||
var finalWalletOutputs = allWalletOutputs.Where(x => !allWalletInputs.Select(y => y.Outpoint).Contains(x.Outpoint)).OrderByDescending(x => x.Amount).ToList();
|
||||
|
||||
var allInputs = transaction.ForeignInputs.Value.Union(transaction.Children.SelectMany(x => x.ForeignInputs.Value)).ToList();
|
||||
var allOutputs = transaction.ForeignOutputs.Value.Union(transaction.Children.SelectMany(x => x.ForeignOutputs.Value)).ToList();
|
||||
var allInputs = transaction.ForeignInputs.Value
|
||||
.Union(transaction.Children.SelectMany(x => x.ForeignInputs.Value))
|
||||
.Union(allWalletInputs.Select(x => x.Outpoint))
|
||||
.ToList();
|
||||
|
||||
var allOutputs = transaction.ForeignOutputs.Value.Select(x => new OutPoint(x.Transaction.GetHash(), x.N))
|
||||
.Union(transaction.Children.SelectMany(x => x.ForeignOutputs.Value.Select(y => new OutPoint(y.Transaction.GetHash(), y.N))))
|
||||
.Union(allWalletOutputs.Select(x => x.Outpoint))
|
||||
.ToList();
|
||||
|
||||
var freshInputs = allInputs.Where(x => !allOutputs.Contains(x));
|
||||
var finalOutputs = allOutputs.Where(x => !allInputs.Contains(x));
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@ using WalletWasabi.Fluent.Extensions;
|
|||
using WalletWasabi.Fluent.Models.Wallets;
|
||||
using WalletWasabi.Fluent.Models.UI;
|
||||
using WalletWasabi.Fluent.ViewModels.Navigation;
|
||||
using WalletWasabi.Fluent.ViewModels.Wallets.Transactions.Inputs;
|
||||
using WalletWasabi.Fluent.ViewModels.Wallets.Transactions.Outputs;
|
||||
|
||||
namespace WalletWasabi.Fluent.ViewModels.Wallets.Home.History.Details;
|
||||
|
||||
|
@ -38,6 +40,11 @@ public partial class TransactionDetailsViewModel : RoutableViewModel
|
|||
UiContext = uiContext;
|
||||
_wallet = wallet;
|
||||
|
||||
InputList = new InputsCoinListViewModel(model.WalletInputs, model.WalletInputs.Count + model.ForeignInputs.Value.Count);
|
||||
OutputList = new OutputsCoinListViewModel(
|
||||
model.WalletOutputs.Select(x => x.TxOut).ToList(),
|
||||
model.ForeignOutputs.Value.Select(x => x.TxOut).ToList());
|
||||
|
||||
NextCommand = ReactiveCommand.Create(OnNext);
|
||||
Fee = wallet.AmountProvider.Create(model.Fee);
|
||||
IsFeeVisible = model.Fee != null;
|
||||
|
@ -50,6 +57,9 @@ public partial class TransactionDetailsViewModel : RoutableViewModel
|
|||
Task.Run(() => UpdateValuesAsync(model, CancellationToken.None));
|
||||
}
|
||||
|
||||
public InputsCoinListViewModel InputList { get; }
|
||||
public OutputsCoinListViewModel OutputList { get; }
|
||||
|
||||
public BitcoinAddress? SingleAddress { get; set; }
|
||||
|
||||
public uint256 TransactionId { get; }
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
using System.Linq;
|
||||
using NBitcoin;
|
||||
using WalletWasabi.Blockchain.Analysis.Clustering;
|
||||
using WalletWasabi.Blockchain.TransactionBuilding;
|
||||
using WalletWasabi.Fluent.Extensions;
|
||||
using WalletWasabi.Fluent.Helpers;
|
||||
using WalletWasabi.Fluent.Models.Wallets;
|
||||
using WalletWasabi.Fluent.ViewModels.Wallets.Transactions.Inputs;
|
||||
using WalletWasabi.Fluent.ViewModels.Wallets.Transactions.Outputs;
|
||||
using WalletWasabi.Wallets;
|
||||
|
||||
namespace WalletWasabi.Fluent.ViewModels.Wallets.Send;
|
||||
|
@ -24,6 +27,8 @@ public partial class TransactionSummaryViewModel : ViewModelBase
|
|||
[AutoNotify] private FeeRate? _feeRate;
|
||||
[AutoNotify] private double? _amountDiff;
|
||||
[AutoNotify] private double? _feeDiff;
|
||||
[AutoNotify] private InputsCoinListViewModel? _inputList;
|
||||
[AutoNotify] private OutputsCoinListViewModel? _outputList;
|
||||
|
||||
private TransactionSummaryViewModel(TransactionPreviewViewModel parent, IWalletModel wallet, TransactionInfo info, bool isPreview = false)
|
||||
{
|
||||
|
@ -52,11 +57,26 @@ public partial class TransactionSummaryViewModel : ViewModelBase
|
|||
ConfirmationTime = _wallet.Transactions.TryEstimateConfirmationTime(info);
|
||||
|
||||
var destinationAmount = _transaction.CalculateDestinationAmount(info.Destination);
|
||||
var destinationIndexedTxOut =
|
||||
transactionResult.Transaction.ForeignOutputs.Select(x => x.TxOut)
|
||||
.Union(transactionResult.Transaction.WalletOutputs.Select(x => x.TxOut))
|
||||
.FirstOrDefault(x => x.ScriptPubKey == info.Destination.ScriptPubKey);
|
||||
|
||||
Amount = UiContext.AmountProvider.Create(destinationAmount);
|
||||
Fee = UiContext.AmountProvider.Create(_transaction.Fee);
|
||||
FeeRate = info.FeeRate;
|
||||
|
||||
InputList = new InputsCoinListViewModel(transactionResult.Transaction.WalletInputs,
|
||||
transactionResult.Transaction.WalletInputs.Count + transactionResult.Transaction.ForeignInputs.Count,
|
||||
Parent.CurrentTransactionSummary.InputList?.TreeDataGridSource.Items.First().IsExpanded,
|
||||
!IsPreview ? null : Parent.CurrentTransactionSummary.InputList?.TreeDataGridSource.Items.First().Children.Count);
|
||||
|
||||
OutputList = new OutputsCoinListViewModel(transactionResult.Transaction.WalletOutputs.Select(x => x.TxOut).ToList(),
|
||||
transactionResult.Transaction.ForeignOutputs.Select(x => x.TxOut).ToList(),
|
||||
destinationIndexedTxOut?.ScriptPubKey,
|
||||
Parent.CurrentTransactionSummary.OutputList?.TreeDataGridSource.Items.First().IsExpanded,
|
||||
!IsPreview ? null : Parent.CurrentTransactionSummary.OutputList?.TreeDataGridSource.Items.First().Children.Count);
|
||||
|
||||
Recipient = info.Recipient;
|
||||
IsCustomFeeUsed = info.IsCustomFeeUsed;
|
||||
IsOtherPocketSelectionPossible = info.IsOtherPocketSelectionPossible;
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
using System.Collections.Generic;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Models.TreeDataGrid;
|
||||
using Avalonia.Controls.Templates;
|
||||
using WalletWasabi.Fluent.Helpers;
|
||||
using WalletWasabi.Fluent.Views.Wallets.Transactions.Inputs.Cells;
|
||||
using WalletWasabi.Fluent.Views.Wallets.Transactions.Inputs.Columns;
|
||||
|
||||
namespace WalletWasabi.Fluent.ViewModels.Wallets.Transactions.Inputs;
|
||||
|
||||
public static class InputsCoinListDataGridSource
|
||||
{
|
||||
public static HierarchicalTreeDataGridSource<InputsCoinListItem> Create(IEnumerable<InputsCoinListItem> source)
|
||||
{
|
||||
// [Column] [View] [Header] [Width] [MinWidth] [MaxWidth] [CanUserSort]
|
||||
// AnonymityScore AnonymityColumnView <custom> 3 Star - - true
|
||||
// Amount AmountColumnView Amount 10 Star - - true
|
||||
var result = new HierarchicalTreeDataGridSource<InputsCoinListItem>(source)
|
||||
{
|
||||
Columns =
|
||||
{
|
||||
AnonymityScoreColumn(),
|
||||
AmountColumn(),
|
||||
}
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
private static IColumn<InputsCoinListItem> AmountColumn()
|
||||
{
|
||||
return new TemplateColumn<InputsCoinListItem>(
|
||||
null,
|
||||
new FuncDataTemplate<InputsCoinListItem>((item, _) => item is { IsChild: true } ? new AmountCellView() : new TextCellView()),
|
||||
null,
|
||||
new GridLength(10, GridUnitType.Star),
|
||||
new TemplateColumnOptions<InputsCoinListItem>
|
||||
{
|
||||
CompareAscending = Sort<InputsCoinListItem>.Ascending(x => x.Amount),
|
||||
CompareDescending = Sort<InputsCoinListItem>.Descending(x => x.Amount)
|
||||
});
|
||||
}
|
||||
|
||||
private static IColumn<InputsCoinListItem> AnonymityScoreColumn()
|
||||
{
|
||||
return new HierarchicalExpanderColumn<InputsCoinListItem>(
|
||||
new TemplateColumn<InputsCoinListItem>(
|
||||
null,
|
||||
new FuncDataTemplate<InputsCoinListItem>((_, _) => new AnonymityScoreColumnView(), true),
|
||||
null,
|
||||
new GridLength(3, GridUnitType.Star),
|
||||
new TemplateColumnOptions<InputsCoinListItem>
|
||||
{
|
||||
CompareAscending = Sort<InputsCoinListItem>.Ascending(b => b.AnonymityScore),
|
||||
CompareDescending = Sort<InputsCoinListItem>.Descending(b => b.AnonymityScore)
|
||||
}),
|
||||
group => group.Children,
|
||||
node => true,
|
||||
node => node.IsExpanded);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
using Avalonia;
|
||||
using NBitcoin;
|
||||
using ReactiveUI;
|
||||
using WalletWasabi.Fluent.Models.Wallets;
|
||||
using WalletWasabi.Fluent.TreeDataGrid;
|
||||
|
||||
namespace WalletWasabi.Fluent.ViewModels.Wallets.Transactions.Inputs;
|
||||
|
||||
public abstract partial class InputsCoinListItem : ViewModelBase, ITreeDataGridExpanderItem, IDisposable
|
||||
{
|
||||
protected readonly CompositeDisposable _disposables = new();
|
||||
|
||||
[AutoNotify] private bool _isParentPointerOver;
|
||||
[AutoNotify] private bool _isControlPointerOver;
|
||||
[AutoNotify] private bool _isExpanded;
|
||||
|
||||
protected InputsCoinListItem()
|
||||
{
|
||||
this.WhenAnyValue(x => x.IsControlPointerOver)
|
||||
.Do(x =>
|
||||
{
|
||||
foreach (var child in Children)
|
||||
{
|
||||
child.IsParentPointerOver = x;
|
||||
}
|
||||
})
|
||||
.Subscribe();
|
||||
|
||||
this.WhenAnyValue(x => x.IsExpanded)
|
||||
.Do(x =>
|
||||
{
|
||||
foreach (var child in Children)
|
||||
{
|
||||
child.IsExpanded = x;
|
||||
}
|
||||
})
|
||||
.Subscribe();
|
||||
}
|
||||
|
||||
public Amount Amount { get; protected set; } = new(Money.Zero);
|
||||
public int? AnonymityScore { get; protected set; }
|
||||
public int? TotalInputs { get; set; }
|
||||
public IReadOnlyCollection<InputsCoinViewModel> Children { get; protected set; } = new List<InputsCoinViewModel>();
|
||||
public bool HasChildren => Children.Count > 0;
|
||||
|
||||
public bool IsChild { get; set; }
|
||||
public bool IsLastChild { get; set; }
|
||||
public bool IsParentSelected { get; set; } = false;
|
||||
|
||||
public string TitleText { get; set; }
|
||||
public int? NbDiff { get; set; }
|
||||
public string? Tip { get; set; }
|
||||
|
||||
public void Dispose() => _disposables.Dispose();
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Disposables;
|
||||
using Avalonia.Controls;
|
||||
using WalletWasabi.Blockchain.TransactionOutputs;
|
||||
using WalletWasabi.Fluent.Models.Wallets;
|
||||
using WalletWasabi.Fluent.ViewModels.Wallets.Coinjoins;
|
||||
|
||||
namespace WalletWasabi.Fluent.ViewModels.Wallets.Transactions.Inputs;
|
||||
|
||||
public class InputsCoinListViewModel : ViewModelBase, IDisposable
|
||||
{
|
||||
private readonly CompositeDisposable _disposables = new();
|
||||
|
||||
public InputsCoinListViewModel(IEnumerable<SmartCoin> availableCoins, int inputCount, bool? isExpanded = null, int? oldInputCount = null)
|
||||
{
|
||||
var coinItems = availableCoins.OrderByDescending(x => x.Amount).Select(x => new InputsCoinViewModel(x)).ToList();
|
||||
foreach (var coin in coinItems)
|
||||
{
|
||||
coin.IsChild = true;
|
||||
}
|
||||
|
||||
if (coinItems.Count > 0)
|
||||
{
|
||||
coinItems.Last().IsLastChild = true;
|
||||
}
|
||||
|
||||
if (oldInputCount is not null)
|
||||
{
|
||||
NbDiff = inputCount - oldInputCount;
|
||||
}
|
||||
|
||||
var parentItem = new InputsCoinViewModel(coinItems.ToArray(), inputCount, isExpanded.GetValueOrDefault(), NbDiff);
|
||||
coinItems.Insert(0, parentItem);
|
||||
_disposables.Add(parentItem);
|
||||
|
||||
TreeDataGridSource = InputsCoinListDataGridSource.Create(new List<InputsCoinListItem> { parentItem });
|
||||
TreeDataGridSource.DisposeWith(_disposables);
|
||||
}
|
||||
|
||||
public int? NbDiff { get; }
|
||||
|
||||
public HierarchicalTreeDataGridSource<InputsCoinListItem> TreeDataGridSource { get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_disposables.Dispose();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
using System.Linq;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using WalletWasabi.Blockchain.TransactionOutputs;
|
||||
using WalletWasabi.Fluent.Models.Wallets;
|
||||
|
||||
namespace WalletWasabi.Fluent.ViewModels.Wallets.Transactions.Inputs;
|
||||
|
||||
public class InputsCoinViewModel : InputsCoinListItem
|
||||
{
|
||||
public InputsCoinViewModel(SmartCoin coin)
|
||||
{
|
||||
Coin = coin;
|
||||
Amount = new Amount(coin.Amount);
|
||||
if(coin.HdPubKey.HistoricalAnonSet.TryGetValue(coin.Outpoint.Hash, out var anonSetWhenTxProcessed))
|
||||
{
|
||||
AnonymityScore = (int)anonSetWhenTxProcessed;
|
||||
}
|
||||
else
|
||||
{
|
||||
AnonymityScore = (int)coin.AnonymitySet;
|
||||
}
|
||||
}
|
||||
|
||||
public InputsCoinViewModel(InputsCoinViewModel[] coins, int inputCount, bool isExpanded, int? nbDiff)
|
||||
{
|
||||
Amount = new Amount(coins.Sum(x => x.Amount.Btc));
|
||||
Children = coins;
|
||||
TotalInputs = inputCount;
|
||||
IsExpanded = isExpanded;
|
||||
NbDiff = nbDiff;
|
||||
if (IsExpanded)
|
||||
{
|
||||
foreach (var coin in Children)
|
||||
{
|
||||
coin.IsExpanded = true;
|
||||
}
|
||||
}
|
||||
|
||||
TitleText = $"{TotalInputs} input{(TotalInputs == 1 ? "" : "s")}";
|
||||
|
||||
Tip = Children.Count == TotalInputs ?
|
||||
"All inputs belong to one of your opened wallets" :
|
||||
"Only inputs belonging to one of your opened wallets can be shown.";
|
||||
}
|
||||
public SmartCoin? Coin { get; }
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
using System.Collections.Generic;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Models.TreeDataGrid;
|
||||
using Avalonia.Controls.Templates;
|
||||
using WalletWasabi.Fluent.Helpers;
|
||||
using WalletWasabi.Fluent.Views.Wallets.Transactions.Outputs.Cells;
|
||||
using WalletWasabi.Fluent.Views.Wallets.Transactions.Outputs.Columns;
|
||||
|
||||
namespace WalletWasabi.Fluent.ViewModels.Wallets.Transactions.Outputs;
|
||||
|
||||
public static class OutputsCoinListDataGridSource
|
||||
{
|
||||
public static HierarchicalTreeDataGridSource<OutputsCoinListItem> Create(IEnumerable<OutputsCoinListItem> source)
|
||||
{
|
||||
// [Column] [View] [Header] [Width] [MinWidth] [MaxWidth] [CanUserSort]
|
||||
// ChangeIndicator ChangeIndicatorView <custom> 3 Star - - false
|
||||
// Amount AmountColumnView Amount 10 Star - - true
|
||||
var result = new HierarchicalTreeDataGridSource<OutputsCoinListItem>(source)
|
||||
{
|
||||
Columns =
|
||||
{
|
||||
ChangeIndicatorColumn(),
|
||||
AmountColumn(),
|
||||
}
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
private static IColumn<OutputsCoinListItem> AmountColumn()
|
||||
{
|
||||
return new TemplateColumn<OutputsCoinListItem>(
|
||||
null,
|
||||
new FuncDataTemplate<OutputsCoinListItem>((item, _) => item is { IsChild: true } ? new AmountCellView() : new TextCellView()),
|
||||
null,
|
||||
new GridLength(10, GridUnitType.Star),
|
||||
new TemplateColumnOptions<OutputsCoinListItem>
|
||||
{
|
||||
CompareAscending = Sort<OutputsCoinListItem>.Ascending(x => x.Amount),
|
||||
CompareDescending = Sort<OutputsCoinListItem>.Descending(x => x.Amount)
|
||||
});
|
||||
}
|
||||
|
||||
private static IColumn<OutputsCoinListItem> ChangeIndicatorColumn()
|
||||
{
|
||||
return new HierarchicalExpanderColumn<OutputsCoinListItem>(
|
||||
new TemplateColumn<OutputsCoinListItem>(
|
||||
null,
|
||||
new FuncDataTemplate<OutputsCoinListItem>((_, _) => new IsOwnIndicatorView(), true),
|
||||
null,
|
||||
new GridLength(3, GridUnitType.Star)),
|
||||
group => group.Children,
|
||||
node => node.HasChildren,
|
||||
node => node.IsExpanded);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
using NBitcoin;
|
||||
using ReactiveUI;
|
||||
using WalletWasabi.Fluent.Models.Wallets;
|
||||
using WalletWasabi.Fluent.TreeDataGrid;
|
||||
|
||||
namespace WalletWasabi.Fluent.ViewModels.Wallets.Transactions.Outputs;
|
||||
|
||||
public abstract partial class OutputsCoinListItem : ViewModelBase, ITreeDataGridExpanderItem, IDisposable
|
||||
{
|
||||
protected readonly CompositeDisposable _disposables = new();
|
||||
|
||||
[AutoNotify] private bool _isParentPointerOver;
|
||||
[AutoNotify] private bool _isControlPointerOver;
|
||||
[AutoNotify] private bool _isExpanded;
|
||||
|
||||
protected OutputsCoinListItem()
|
||||
{
|
||||
this.WhenAnyValue(x => x.IsControlPointerOver)
|
||||
.Do(x =>
|
||||
{
|
||||
foreach (var child in Children)
|
||||
{
|
||||
child.IsParentPointerOver = x;
|
||||
}
|
||||
})
|
||||
.Subscribe();
|
||||
|
||||
this.WhenAnyValue(x => x.IsExpanded)
|
||||
.Do(x =>
|
||||
{
|
||||
foreach (var child in Children)
|
||||
{
|
||||
child.IsExpanded = x;
|
||||
}
|
||||
})
|
||||
.Subscribe();
|
||||
}
|
||||
|
||||
public Amount Amount { get; protected set; } = new(Money.Zero);
|
||||
public bool ShowOwn { get; protected set; }
|
||||
public bool ShowChange { get; protected set; }
|
||||
public int? TotalOutputs { get; set; }
|
||||
public IReadOnlyCollection<OutputsCoinViewModel> Children { get; protected set; } = new List<OutputsCoinViewModel>();
|
||||
public bool HasChildren => Children.Count > 0;
|
||||
public bool IsChild { get; set; }
|
||||
public bool IsLastChild { get; set; }
|
||||
public bool IsParentSelected { get; set; } = false;
|
||||
|
||||
public string TitleText { get; set; }
|
||||
|
||||
public int? NbDiff { get; set; }
|
||||
|
||||
public void Dispose() => _disposables.Dispose();
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Disposables;
|
||||
using Avalonia.Controls;
|
||||
using NBitcoin;
|
||||
using WalletWasabi.Fluent.Models.Wallets;
|
||||
|
||||
namespace WalletWasabi.Fluent.ViewModels.Wallets.Transactions.Outputs;
|
||||
|
||||
public class OutputsCoinListViewModel : ViewModelBase, IDisposable
|
||||
{
|
||||
private readonly CompositeDisposable _disposables = new();
|
||||
|
||||
public OutputsCoinListViewModel(List<TxOut> ownOutputs, List<TxOut> foreignOutputs, Script? destinationScript = null, bool? isExpanded = null, int? oldOutputCount = null)
|
||||
{
|
||||
|
||||
var outputCount = ownOutputs.Count + foreignOutputs.Count;
|
||||
|
||||
var coinItems = ownOutputs
|
||||
.Union(foreignOutputs)
|
||||
.OrderByDescending(x => x.Value)
|
||||
.Select(x =>
|
||||
new OutputsCoinViewModel(
|
||||
x,
|
||||
ownOutputs.Contains(x),
|
||||
destinationScript is not null && x.ScriptPubKey != destinationScript))
|
||||
.ToList();
|
||||
|
||||
foreach (var coin in coinItems)
|
||||
{
|
||||
coin.IsChild = true;
|
||||
}
|
||||
|
||||
if (coinItems.Count > 0)
|
||||
{
|
||||
coinItems.Last().IsLastChild = true;
|
||||
}
|
||||
|
||||
if (oldOutputCount is not null)
|
||||
{
|
||||
NbDiff = outputCount - oldOutputCount;
|
||||
}
|
||||
|
||||
var parentItem = new OutputsCoinViewModel(coinItems.ToArray(), outputCount, isExpanded.GetValueOrDefault(), NbDiff);
|
||||
coinItems.Insert(0, parentItem);
|
||||
_disposables.Add(parentItem);
|
||||
|
||||
TreeDataGridSource = OutputsCoinListDataGridSource.Create(new List<OutputsCoinListItem> { parentItem });
|
||||
TreeDataGridSource.DisposeWith(_disposables);
|
||||
}
|
||||
|
||||
public int? NbDiff { get; }
|
||||
|
||||
public HierarchicalTreeDataGridSource<OutputsCoinListItem> TreeDataGridSource { get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_disposables.Dispose();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
using System.Linq;
|
||||
using NBitcoin;
|
||||
using WalletWasabi.Fluent.Models.Wallets;
|
||||
|
||||
namespace WalletWasabi.Fluent.ViewModels.Wallets.Transactions.Outputs;
|
||||
|
||||
public class OutputsCoinViewModel : OutputsCoinListItem
|
||||
{
|
||||
public OutputsCoinViewModel(TxOut txOut, bool isOwn, bool isChange)
|
||||
{
|
||||
TxOut = txOut;
|
||||
Amount = new Amount(txOut.Value);
|
||||
ShowChange = isChange;
|
||||
ShowOwn = !isChange && isOwn;
|
||||
}
|
||||
|
||||
public OutputsCoinViewModel(OutputsCoinViewModel[] coins, int outputCount, bool isExpanded, int? nbDiff)
|
||||
{
|
||||
Amount = new Amount(coins.Sum(x => x.Amount.Btc));
|
||||
Children = coins;
|
||||
TotalOutputs = outputCount;
|
||||
IsExpanded = isExpanded;
|
||||
if (IsExpanded)
|
||||
{
|
||||
foreach (var coin in Children)
|
||||
{
|
||||
coin.IsExpanded = true;
|
||||
}
|
||||
}
|
||||
TitleText = $"{Children.Count} output{(Children.Count == 1 ? "" : "s")}";
|
||||
NbDiff = nbDiff;
|
||||
}
|
||||
public TxOut? TxOut { get; }
|
||||
}
|
|
@ -12,7 +12,8 @@
|
|||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
UseOpacity="True"
|
||||
MaxPrivacyChars="14">
|
||||
MaxPrivacyChars="14"
|
||||
Margin="10 0 0 0">
|
||||
<AmountControl MinWidth="140" Amount="{Binding Amount}"/>
|
||||
</PrivacyContentControl>
|
||||
</UserControl>
|
||||
|
|
|
@ -8,5 +8,5 @@
|
|||
x:DataType="coinjoins:CoinjoinCoinViewModel"
|
||||
x:CompileBindings="True">
|
||||
|
||||
<TextBlock Margin="-57 0 0 0" HorizontalAlignment="Center" VerticalAlignment="Center" Classes="bold" Text="{Binding TitleText}"/>
|
||||
<TextBlock Margin="-52 0 0 0" HorizontalAlignment="Center" VerticalAlignment="Center" Classes="bold" Text="{Binding TitleText}"/>
|
||||
</UserControl>
|
||||
|
|
|
@ -51,20 +51,15 @@
|
|||
<Separator />
|
||||
<PreviewItem Icon="{StaticResource input_count}" />
|
||||
<DockPanel Margin="0 -35 0 0" LastChildFill="True">
|
||||
<Grid >
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid ColumnDefinitions="*,30,*">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock Margin="-5 0 0 0" Text="Inputs" FontSize="14" FontWeight="Bold" HorizontalAlignment="Center"/>
|
||||
<coinjoins:CoinjoinCoinListView Margin="15 15 25 0" DataContext="{Binding InputList}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
|
||||
<TextBlock Margin="35 0 25 0" Text="Inputs" FontSize="14" FontWeight="Bold" HorizontalAlignment="Center"/>
|
||||
<coinjoins:CoinjoinCoinListView Margin="10 15 10 0" DataContext="{Binding InputList}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Column="1">
|
||||
<TextBlock Margin="-5 0 0 0" Text="Outputs" FontSize="14" FontWeight="Bold" HorizontalAlignment="Center"/>
|
||||
<coinjoins:CoinjoinCoinListView Margin="15 15 25 0" DataContext="{Binding OutputList}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
|
||||
<PathIcon Grid.Column="1" Opacity="0.6" Margin="0 48 0 0" VerticalAlignment="Top" Data="{StaticResource arrow_right_regular}"/>
|
||||
<StackPanel Grid.Column="2">
|
||||
<TextBlock Margin="35 0 25 0" Text="Outputs" FontSize="14" FontWeight="Bold" HorizontalAlignment="Center"/>
|
||||
<coinjoins:CoinjoinCoinListView Margin="10 15 10 0" DataContext="{Binding OutputList}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
|
|
|
@ -43,20 +43,15 @@
|
|||
<Separator />
|
||||
<PreviewItem Icon="{StaticResource input_count}" />
|
||||
<DockPanel Margin="0 -35 0 0" LastChildFill="True">
|
||||
<Grid >
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid ColumnDefinitions="*,30,*">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock Margin="-5 0 0 0" Text="Inputs" FontSize="14" FontWeight="Bold" HorizontalAlignment="Center"/>
|
||||
<coinjoins:CoinjoinCoinListView Margin="15 15 25 0" DataContext="{Binding InputList}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
|
||||
<TextBlock Margin="35 0 25 0" Text="Inputs" FontSize="14" FontWeight="Bold" HorizontalAlignment="Center"/>
|
||||
<coinjoins:CoinjoinCoinListView Margin="10 15 10 0" DataContext="{Binding InputList}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Column="1">
|
||||
<TextBlock Margin="-5 0 0 0" Text="Outputs" FontSize="14" FontWeight="Bold" HorizontalAlignment="Center"/>
|
||||
<coinjoins:CoinjoinCoinListView Margin="15 15 25 0" DataContext="{Binding OutputList}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
|
||||
<PathIcon Grid.Column="1" Opacity="0.6" Margin="0 48 0 0" VerticalAlignment="Top" Data="{StaticResource arrow_right_regular}"/>
|
||||
<StackPanel Grid.Column="2">
|
||||
<TextBlock Margin="35 0 25 0" Text="Outputs" FontSize="14" FontWeight="Bold" HorizontalAlignment="Center"/>
|
||||
<coinjoins:CoinjoinCoinListView Margin="10 15 10 0" DataContext="{Binding OutputList}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:details="clr-namespace:WalletWasabi.Fluent.ViewModels.Wallets.Home.History.Details"
|
||||
xmlns:conv="clr-namespace:WalletWasabi.Fluent.Converters"
|
||||
xmlns:inputs="clr-namespace:WalletWasabi.Fluent.Views.Wallets.Transactions.Inputs"
|
||||
xmlns:outputs="clr-namespace:WalletWasabi.Fluent.Views.Wallets.Transactions.Outputs"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:DataType="details:TransactionDetailsViewModel"
|
||||
x:CompileBindings="True"
|
||||
|
@ -113,6 +115,22 @@
|
|||
</PrivacyContentControl>
|
||||
</PreviewItem>
|
||||
|
||||
<!-- Inputs/Outputs -->
|
||||
<Separator />
|
||||
<PreviewItem Icon="{StaticResource input_count}" />
|
||||
<DockPanel Margin="30 -30 0 0" LastChildFill="True">
|
||||
<Grid >
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="30"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<inputs:InputsCoinListView Grid.Column="0" Margin="10 -15 10 0" DataContext="{Binding InputList}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
|
||||
<PathIcon Grid.Column="1" Opacity="0.6" Margin="0 2 0 0" VerticalAlignment="Top" Data="{StaticResource arrow_right_regular}"/>
|
||||
<outputs:OutputsCoinListView Grid.Column="2" Margin="10 -15 10 0" DataContext="{Binding OutputList}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
|
||||
<Separator IsVisible="{Binding IsConfirmed}" />
|
||||
|
||||
<!-- Block hash -->
|
||||
|
|
|
@ -81,7 +81,7 @@
|
|||
<ShowFlyoutOnPointerOverBehavior />
|
||||
</Interaction.Behaviors>
|
||||
<FlyoutBase.AttachedFlyout>
|
||||
<Flyout Placement="Bottom" ShowMode="TransientWithDismissOnPointerMoveAway">
|
||||
<Flyout Placement="BottomEdgeAlignedLeft" ShowMode="TransientWithDismissOnPointerMoveAway">
|
||||
<Panel DataContext="{Binding PrivacySuggestions}" Margin="0 5 0 0">
|
||||
<!-- Warnings -->
|
||||
<send:PrivacyWarningsView IsVisible="{Binding !MaxPrivacy}" />
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:send="clr-namespace:WalletWasabi.Fluent.ViewModels.Wallets.Send"
|
||||
xmlns:converters="clr-namespace:WalletWasabi.Fluent.Converters"
|
||||
xmlns:outputs="clr-namespace:WalletWasabi.Fluent.Views.Wallets.Transactions.Outputs"
|
||||
xmlns:inputs="clr-namespace:WalletWasabi.Fluent.Views.Wallets.Transactions.Inputs"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:CompileBindings="True"
|
||||
x:DataType="send:TransactionSummaryViewModel"
|
||||
|
@ -79,6 +81,18 @@
|
|||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center" />
|
||||
</PreviewItem>
|
||||
|
||||
<!-- Inputs/Outputs -->
|
||||
<Separator />
|
||||
<PreviewItem Icon="{StaticResource input_count}" />
|
||||
<DockPanel Margin="30 -30 0 0" LastChildFill="True">
|
||||
<Grid ColumnDefinitions="*,30,*">
|
||||
<inputs:InputsCoinListView Grid.Column="0" Margin="10 -15 10 0" DataContext="{Binding InputList}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
|
||||
<PathIcon Grid.Column="1" Opacity="0.6" Margin="0 2 0 0" VerticalAlignment="Top" Data="{StaticResource arrow_right_regular}"/>
|
||||
<outputs:OutputsCoinListView Grid.Column="2" Margin="10 -15 10 0" DataContext="{Binding OutputList}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
|
||||
<Separator />
|
||||
|
||||
<!-- Fee and confirmation time -->
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:inputs="clr-namespace:WalletWasabi.Fluent.ViewModels.Wallets.Transactions.Inputs"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="WalletWasabi.Fluent.Views.Wallets.Transactions.Inputs.Cells.AmountCellView"
|
||||
x:DataType="inputs:InputsCoinListItem"
|
||||
x:CompileBindings="True">
|
||||
|
||||
<PrivacyContentControl PrivacyReplacementMode="Text"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
UseOpacity="True"
|
||||
MaxPrivacyChars="14"
|
||||
Margin="5 0 0 0">
|
||||
<AmountControl MinWidth="140" Amount="{Binding Amount}"/>
|
||||
</PrivacyContentControl>
|
||||
</UserControl>
|
|
@ -0,0 +1,17 @@
|
|||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace WalletWasabi.Fluent.Views.Wallets.Transactions.Inputs.Cells;
|
||||
|
||||
public class AmountCellView : UserControl
|
||||
{
|
||||
public AmountCellView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:inputs="clr-namespace:WalletWasabi.Fluent.ViewModels.Wallets.Transactions.Inputs"
|
||||
xmlns:converters="clr-namespace:WalletWasabi.Fluent.Converters"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="WalletWasabi.Fluent.Views.Wallets.Transactions.Inputs.Cells.TextCellView"
|
||||
x:DataType="inputs:InputsCoinViewModel"
|
||||
x:CompileBindings="True">
|
||||
<UserControl.Resources>
|
||||
<converters:DiffToBrushConverter x:Key="InvertedDiffConverter"
|
||||
PositiveBrush="{StaticResource UncertainBrush}"
|
||||
NegativeBrush="{StaticResource PositiveBrush}"
|
||||
ZeroBrush="{StaticResource UncertainBrush}" />
|
||||
</UserControl.Resources>
|
||||
|
||||
<StackPanel Orientation="Horizontal" ToolTip.Tip="{Binding Tip, FallbackValue=''}">
|
||||
|
||||
<TextBlock IsVisible="{Binding !!NbDiff}"
|
||||
FontWeight="Bold"
|
||||
Margin="45 0 0 0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center">
|
||||
<TextBlock.Inlines>
|
||||
<Run Text="{Binding TitleText}" />
|
||||
<Run Text=" " />
|
||||
<Run Foreground="{Binding NbDiff, Converter={StaticResource InvertedDiffConverter}}"
|
||||
Text="{Binding NbDiff, Converter={x:Static converters:IntConverter.FPlusConverter}}" />
|
||||
</TextBlock.Inlines>
|
||||
</TextBlock>
|
||||
<TextBlock IsVisible="{Binding NbDiff, Converter={x:Static converters:IntConverter.IsNullOrZero}}"
|
||||
Margin="45 0 0 0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Classes="bold"
|
||||
Text="{Binding TitleText}">
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</UserControl>
|
|
@ -0,0 +1,17 @@
|
|||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace WalletWasabi.Fluent.Views.Wallets.Transactions.Inputs.Cells;
|
||||
|
||||
public class TextCellView : UserControl
|
||||
{
|
||||
public TextCellView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:inputs="clr-namespace:WalletWasabi.Fluent.ViewModels.Wallets.Transactions.Inputs"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:DataType="inputs:InputsCoinListItem"
|
||||
x:CompileBindings="True"
|
||||
x:Class="WalletWasabi.Fluent.Views.Wallets.Transactions.Inputs.Columns.AnonymityScoreColumnView">
|
||||
<StackPanel Margin="10 0 0 0" ToolTip.Tip="Anonymity Score" IsVisible="{Binding !!AnonymityScore}" Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<PathIcon Data="{StaticResource anonscore}" VerticalAlignment="Center" Opacity="0.6" Height="15"/>
|
||||
<TextBlock Text="{Binding AnonymityScore}" VerticalAlignment="Center" FontWeight="Bold" />
|
||||
</StackPanel>
|
||||
</UserControl>
|
|
@ -0,0 +1,17 @@
|
|||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace WalletWasabi.Fluent.Views.Wallets.Transactions.Inputs.Columns;
|
||||
|
||||
public class AnonymityScoreColumnView : UserControl
|
||||
{
|
||||
public AnonymityScoreColumnView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:inputs="clr-namespace:WalletWasabi.Fluent.ViewModels.Wallets.Transactions.Inputs"
|
||||
xmlns:treeDataGrid="clr-namespace:WalletWasabi.Fluent.TreeDataGrid"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="WalletWasabi.Fluent.Views.Wallets.Transactions.Inputs.InputsCoinListView"
|
||||
x:DataType="inputs:InputsCoinListViewModel"
|
||||
x:CompileBindings="True">
|
||||
<Panel>
|
||||
<TreeDataGrid x:Name="TreeDataGrid" Source="{Binding TreeDataGridSource}">
|
||||
<TreeDataGrid.Styles>
|
||||
<Style Selector="treeDataGrid|TreeDataGridAmountPrivacyTextCell">
|
||||
<Setter Property="FontFamily" Value="{StaticResource MonospacedFont}" />
|
||||
<Setter Property="FontWeight" Value="Bold" />
|
||||
<Setter Property="Opacity" Value="1" />
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextForegroundColor}" />
|
||||
</Style>
|
||||
<Style Selector="TreeDataGridExpanderCell">
|
||||
<Setter Property="Theme" Value="{StaticResource CoinjoinCoinsListViewTreeDataGridExpanderCell}" />
|
||||
</Style>
|
||||
<Style Selector="TreeDataGridRow" x:DataType="inputs:InputsCoinListItem">
|
||||
<Setter Property="Theme" Value="{StaticResource CoinjoinCoinsListViewTreeDataGridRow}" />
|
||||
</Style>
|
||||
</TreeDataGrid.Styles>
|
||||
<Interaction.Behaviors>
|
||||
<SetLastChildBehavior />
|
||||
</Interaction.Behaviors>
|
||||
<TreeDataGrid.ElementFactory>
|
||||
<treeDataGrid:PrivacyElementFactory />
|
||||
</TreeDataGrid.ElementFactory>
|
||||
</TreeDataGrid>
|
||||
</Panel>
|
||||
</UserControl>
|
|
@ -0,0 +1,17 @@
|
|||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace WalletWasabi.Fluent.Views.Wallets.Transactions.Inputs;
|
||||
|
||||
public class InputsCoinListView : UserControl
|
||||
{
|
||||
public InputsCoinListView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:outputs="clr-namespace:WalletWasabi.Fluent.ViewModels.Wallets.Transactions.Outputs"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="WalletWasabi.Fluent.Views.Wallets.Transactions.Outputs.Cells.AmountCellView"
|
||||
x:DataType="outputs:OutputsCoinListItem"
|
||||
x:CompileBindings="True">
|
||||
|
||||
<PrivacyContentControl PrivacyReplacementMode="Text"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
UseOpacity="True"
|
||||
MaxPrivacyChars="14"
|
||||
Margin="5 0 0 0">
|
||||
<AmountControl MinWidth="140" Amount="{Binding Amount}"/>
|
||||
</PrivacyContentControl>
|
||||
</UserControl>
|
|
@ -0,0 +1,17 @@
|
|||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace WalletWasabi.Fluent.Views.Wallets.Transactions.Outputs.Cells;
|
||||
|
||||
public class AmountCellView : UserControl
|
||||
{
|
||||
public AmountCellView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:outputs="clr-namespace:WalletWasabi.Fluent.ViewModels.Wallets.Transactions.Outputs"
|
||||
xmlns:converters="clr-namespace:WalletWasabi.Fluent.Converters"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="WalletWasabi.Fluent.Views.Wallets.Transactions.Outputs.Cells.TextCellView"
|
||||
x:DataType="outputs:OutputsCoinViewModel"
|
||||
x:CompileBindings="True">
|
||||
<UserControl.Resources>
|
||||
<converters:DiffToBrushConverter x:Key="InvertedDiffConverter"
|
||||
PositiveBrush="{StaticResource UncertainBrush}"
|
||||
NegativeBrush="{StaticResource PositiveBrush}"
|
||||
ZeroBrush="{StaticResource UncertainBrush}" />
|
||||
</UserControl.Resources>
|
||||
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock IsVisible="{Binding !!NbDiff}"
|
||||
FontWeight="Bold"
|
||||
Margin="45 0 0 0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center">
|
||||
<TextBlock.Inlines>
|
||||
<Run Text="{Binding TitleText}" />
|
||||
<Run Text=" " />
|
||||
<Run Foreground="{Binding NbDiff, Converter={StaticResource InvertedDiffConverter}}"
|
||||
Text="{Binding NbDiff, Converter={x:Static converters:IntConverter.FPlusConverter}}" />
|
||||
</TextBlock.Inlines>
|
||||
</TextBlock>
|
||||
<TextBlock IsVisible="{Binding NbDiff, Converter={x:Static converters:IntConverter.IsNullOrZero}}"
|
||||
Margin="45 0 0 0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Classes="bold"
|
||||
Text="{Binding TitleText}">
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</UserControl>
|
|
@ -0,0 +1,17 @@
|
|||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace WalletWasabi.Fluent.Views.Wallets.Transactions.Outputs.Cells;
|
||||
|
||||
public class TextCellView : UserControl
|
||||
{
|
||||
public TextCellView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:outputs="clr-namespace:WalletWasabi.Fluent.ViewModels.Wallets.Transactions.Outputs"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:DataType="outputs:OutputsCoinListItem"
|
||||
x:CompileBindings="True"
|
||||
x:Class="WalletWasabi.Fluent.Views.Wallets.Transactions.Outputs.Columns.IsOwnIndicatorView">
|
||||
|
||||
<InvalidatingStackPanel Orientation="Horizontal" Spacing="2" HorizontalAlignment="Right">
|
||||
<Border BorderThickness="1"
|
||||
ToolTip.Tip="This output belongs to one of your opened wallets"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0 0 8 0"
|
||||
IsVisible="{Binding ShowOwn}"
|
||||
BorderBrush="{DynamicResource TextControlForeground}" Opacity="0.6" CornerRadius="4" Padding="3">
|
||||
<TextBlock Classes="h10" Text="OWN"/>
|
||||
</Border>
|
||||
<Border BorderThickness="1"
|
||||
ToolTip.Tip="This output goes back to your own wallet"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding ShowChange}"
|
||||
BorderBrush="{DynamicResource TextControlForeground}" Opacity="0.6" CornerRadius="4" Padding="3">
|
||||
<TextBlock Classes="h10" Text="CHANGE"/>
|
||||
</Border>
|
||||
</InvalidatingStackPanel>
|
||||
</UserControl>
|
|
@ -0,0 +1,17 @@
|
|||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace WalletWasabi.Fluent.Views.Wallets.Transactions.Outputs.Columns;
|
||||
|
||||
public class IsOwnIndicatorView : UserControl
|
||||
{
|
||||
public IsOwnIndicatorView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:outputs="clr-namespace:WalletWasabi.Fluent.ViewModels.Wallets.Transactions.Outputs"
|
||||
xmlns:treeDataGrid="clr-namespace:WalletWasabi.Fluent.TreeDataGrid"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="WalletWasabi.Fluent.Views.Wallets.Transactions.Outputs.OutputsCoinListView"
|
||||
x:DataType="outputs:OutputsCoinListViewModel"
|
||||
x:CompileBindings="True">
|
||||
<Panel>
|
||||
<TreeDataGrid x:Name="TreeDataGrid" Source="{Binding TreeDataGridSource}">
|
||||
<TreeDataGrid.Styles>
|
||||
<Style Selector="treeDataGrid|TreeDataGridAmountPrivacyTextCell">
|
||||
<Setter Property="FontFamily" Value="{StaticResource MonospacedFont}" />
|
||||
<Setter Property="FontWeight" Value="Bold" />
|
||||
<Setter Property="Opacity" Value="1" />
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextForegroundColor}" />
|
||||
</Style>
|
||||
<Style Selector="TreeDataGridExpanderCell">
|
||||
<Setter Property="Theme" Value="{StaticResource CoinjoinCoinsListViewTreeDataGridExpanderCell}" />
|
||||
</Style>
|
||||
<Style Selector="TreeDataGridRow" x:DataType="outputs:OutputsCoinListItem">
|
||||
<Setter Property="Theme" Value="{StaticResource CoinjoinCoinsListViewTreeDataGridRow}" />
|
||||
</Style>
|
||||
</TreeDataGrid.Styles>
|
||||
<Interaction.Behaviors>
|
||||
<SetLastChildBehavior />
|
||||
</Interaction.Behaviors>
|
||||
<TreeDataGrid.ElementFactory>
|
||||
<treeDataGrid:PrivacyElementFactory />
|
||||
</TreeDataGrid.ElementFactory>
|
||||
</TreeDataGrid>
|
||||
</Panel>
|
||||
</UserControl>
|
|
@ -0,0 +1,17 @@
|
|||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace WalletWasabi.Fluent.Views.Wallets.Transactions.Outputs;
|
||||
|
||||
public class OutputsCoinListView : UserControl
|
||||
{
|
||||
public OutputsCoinListView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
|
@ -22,7 +22,7 @@ public class TransactionSummary
|
|||
|
||||
public Func<IReadOnlyCollection<OutPoint>> ForeignInputs => () => Transaction.ForeignInputs.Select(x => x.PrevOut).ToArray();
|
||||
public IReadOnlyCollection<SmartCoin> WalletInputs => Transaction.WalletInputs;
|
||||
public Func<IReadOnlyCollection<OutPoint>> ForeignOutputs => () => Transaction.ForeignOutputs.Select(x => new OutPoint(GetHash(), x.N)).ToArray();
|
||||
public Func<IReadOnlyCollection<IndexedTxOut>> ForeignOutputs => () => Transaction.ForeignOutputs;
|
||||
public IReadOnlyCollection<SmartCoin> WalletOutputs => Transaction.WalletOutputs;
|
||||
public DateTimeOffset FirstSeen => Transaction.FirstSeen;
|
||||
public LabelsArray Labels => Transaction.Labels;
|
||||
|
|
Загрузка…
Ссылка в новой задаче