feat(app): transaction detail overlay with full on-chain data

Double-clicking a history row opens an in-app overlay (same pattern as
address/server/settings overlays) that shows all available data for a
transaction. The overlay appears immediately with a spinner; data is
fetched from the server in the background and rendered once ready.
Backdrop tap and Esc close it; a pending fetch is cancelled.

New Core type — TransactionInspector (Core/Wallet/):
- FetchAsync() downloads the raw tx, all parent txs (parallel requests,
  one round-trip instead of N sequential), and the block header
- Reconstructs inputs (amounts, addresses, is-mine), outputs, fee,
  virtual size, RBF flag, block timestamp, confirmations

New App types:
- TransactionDetails record: full parsed data (fee, net, I/O lists,
  counterparty addresses, coinbase detection…)
- TransactionDetailsViewModel: pre-formats all strings in the current
  locale and unit (status, date, signed amounts, sat/vB rate…)
- TxIoRow record: one row in the input/output tables
- MineColorConverter: bool → brush (MediumSeaGreen for own addresses)

ViewModel changes:
- ShowTransactionDetailsAsync / CloseTransactionDetailsCommand
- BuildTransactionDetailsAsync (also public for future RBF/CPFP use)
- CancellationTokenSource so opening a new tx cancels the previous fetch
- All server work runs on Task.Run to keep the UI thread free

XAML: history rows get DoubleTapped handler, Hand cursor, hint label,
txid now uses TextTrimming instead of SelectableTextBlock.

i18n: 24 new tx.* / history.hint keys in all 6 languages.
This commit is contained in:
2026-06-12 11:47:23 +02:00
parent 4c7e8696cb
commit 4735490759
7 changed files with 633 additions and 13 deletions
+22
View File
@@ -0,0 +1,22 @@
using System;
using System.Globalization;
using Avalonia;
using Avalonia.Data.Converters;
using Avalonia.Media;
namespace PalladiumWallet.App.ViewModels;
/// <summary>
/// bool → pennello: gli indirizzi del wallet (input/output "nostri") sono
/// evidenziati in verde, gli altri usano il colore di testo predefinito.
/// </summary>
public sealed class MineColorConverter : IValueConverter
{
public static readonly MineColorConverter Instance = new();
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) =>
value is true ? Brushes.MediumSeaGreen : AvaloniaProperty.UnsetValue;
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) =>
throw new NotSupportedException();
}