feat(ui): wizard flows for xpub/xprv and WIF import
Add two new import paths to the setup wizard: - "Importa xpub / xprv": accepts any SLIP-132 key (xpub/ypub/zpub or xprv/yprv/zprv), auto-detects script type from the version prefix, and saves a watch-only or fully-spendable HD account accordingly. - "Importa chiave WIF": accepts one or more WIF private keys (one per line), goes through script-type selection, and saves an ImportedKeyAccount. WizardFlowKind enum (New, Restore, ImportXkey, ImportWif) governs which steps are shown; WizardBack() updated for all four flows. OpenLoaded() updated to accept IWalletAccount and display wallet kind tags (watch-only / imported) in the network info line. Localization keys added for all six languages (it/en/es/fr/pt/de). ViewModel._account changed from HdAccount? to IWalletAccount?.
This commit is contained in:
@@ -78,6 +78,8 @@ public sealed class Loc
|
||||
["wiz.open.btn"] = ["Apri il wallet esistente", "Open existing wallet", "Abrir wallet existente", "Ouvrir le wallet existant", "Abrir carteira existente", "Vorhandenes Wallet öffnen"],
|
||||
["wiz.new.btn"] = ["Crea un nuovo wallet", "Create a new wallet", "Crear nuevo wallet", "Créer un nouveau wallet", "Criar nova carteira", "Neues Wallet erstellen"],
|
||||
["wiz.restore.btn"] = ["Ripristina da seed", "Restore from seed", "Restaurar desde semilla", "Restaurer depuis la graine", "Restaurar da semente", "Aus Seed wiederherstellen"],
|
||||
["wiz.importxkey.btn"] = ["Importa xpub / xprv", "Import xpub / xprv", "Importar xpub / xprv", "Importer xpub / xprv", "Importar xpub / xprv", "xpub / xprv importieren"],
|
||||
["wiz.importwif.btn"] = ["Importa chiave WIF", "Import WIF key", "Importar clave WIF", "Importer clé WIF", "Importar chave WIF", "WIF-Schlüssel importieren"],
|
||||
["wiz.open.title"] = ["Apri il wallet", "Open the wallet", "Abrir el wallet", "Ouvrir le wallet", "Abrir a carteira", "Wallet öffnen"],
|
||||
["wiz.open.placeholder"] = ["Password del file (vuoto se non impostata)", "File password (empty if not set)", "Contraseña del archivo (vacío si no establecida)", "Mot de passe du fichier (vide si non défini)", "Senha do arquivo (vazio se não definida)", "Dateipasswort (leer lassen, wenn nicht gesetzt)"],
|
||||
["wiz.open.ok"] = ["Apri", "Open", "Abrir", "Ouvrir", "Abrir", "Öffnen"],
|
||||
@@ -122,6 +124,30 @@ public sealed class Loc
|
||||
["wiz.scripttype.wrapped.desc"] = ["BIP49 · m/49'/… · indirizzi 3", "BIP49 · m/49'/… · 3 addresses", "BIP49 · m/49'/… · direcciones 3", "BIP49 · m/49'/… · adresses 3", "BIP49 · m/49'/… · endereços 3", "BIP49 · m/49'/… · 3-Adressen"],
|
||||
["wiz.scripttype.native.desc"] = ["BIP84 · m/84'/… · indirizzi plm1q — consigliato", "BIP84 · m/84'/… · plm1q addresses — recommended", "BIP84 · m/84'/… · direcciones plm1q — recomendado", "BIP84 · m/84'/… · adresses plm1q — recommandé", "BIP84 · m/84'/… · endereços plm1q — recomendado", "BIP84 · m/84'/… · plm1q-Adressen — empfohlen"],
|
||||
["wiz.scripttype.taproot.desc"] = ["BIP86 · m/86'/… · indirizzi plm1p", "BIP86 · m/86'/… · plm1p addresses", "BIP86 · m/86'/… · direcciones plm1p", "BIP86 · m/86'/… · adresses plm1p", "BIP86 · m/86'/… · endereços plm1p", "BIP86 · m/86'/… · plm1p-Adressen"],
|
||||
["wiz.importxkey.title"] = ["Importa chiave estesa", "Import extended key", "Importar clave extendida", "Importer la clé étendue", "Importar chave estendida", "Erweiterten Schlüssel importieren"],
|
||||
["wiz.importxkey.hint"] = [
|
||||
"Incolla una xpub/zpub/ypub (watch-only) o xprv/zprv/yprv (spendibile). Il tipo di script viene rilevato automaticamente.",
|
||||
"Paste an xpub/zpub/ypub (watch-only) or xprv/zprv/yprv (spendable). The script type is detected automatically.",
|
||||
"Pega un xpub/zpub/ypub (solo lectura) o xprv/zprv/yprv (gastable). El tipo de script se detecta automáticamente.",
|
||||
"Collez un xpub/zpub/ypub (lecture seule) ou xprv/zprv/yprv (dépensable). Le type de script est détecté automatiquement.",
|
||||
"Cole um xpub/zpub/ypub (somente leitura) ou xprv/zprv/yprv (gastável). O tipo de script é detectado automaticamente.",
|
||||
"Fügen Sie einen xpub/zpub/ypub (nur lesend) oder xprv/zprv/yprv (ausgabefähig) ein. Der Skripttyp wird automatisch erkannt."],
|
||||
["wiz.importxkey.placeholder"] = ["xpub… / zpub… / ypub… / xprv… / zprv…", "xpub… / zpub… / ypub… / xprv… / zprv…", "xpub… / zpub… / ypub… / xprv… / zprv…", "xpub… / zpub… / ypub… / xprv… / zprv…", "xpub… / zpub… / ypub… / xprv… / zprv…", "xpub… / zpub… / ypub… / xprv… / zprv…"],
|
||||
["wiz.importwif.title"] = ["Importa chiave privata WIF", "Import WIF private key", "Importar clave privada WIF", "Importer la clé privée WIF", "Importar chave privada WIF", "WIF-Privatschlüssel importieren"],
|
||||
["wiz.importwif.hint"] = [
|
||||
"Incolla una o più chiavi WIF (una per riga). Puoi importare più chiavi per controllare più indirizzi con lo stesso wallet.",
|
||||
"Paste one or more WIF keys (one per line). You can import multiple keys to control multiple addresses with the same wallet.",
|
||||
"Pega una o más claves WIF (una por línea). Puedes importar múltiples claves para controlar múltiples direcciones con el mismo wallet.",
|
||||
"Collez une ou plusieurs clés WIF (une par ligne). Vous pouvez importer plusieurs clés pour contrôler plusieurs adresses avec le même wallet.",
|
||||
"Cole uma ou mais chaves WIF (uma por linha). Você pode importar várias chaves para controlar vários endereços com a mesma carteira.",
|
||||
"Fügen Sie einen oder mehrere WIF-Schlüssel ein (einer pro Zeile). Sie können mehrere Schlüssel importieren, um mehrere Adressen mit demselben Wallet zu verwalten."],
|
||||
["wiz.importwif.placeholder"] = ["K… / L… / 5… (una chiave per riga)", "K… / L… / 5… (one key per line)", "K… / L… / 5… (una clave por línea)", "K… / L… / 5… (une clé par ligne)", "K… / L… / 5… (uma chave por linha)", "K… / L… / 5… (ein Schlüssel pro Zeile)"],
|
||||
|
||||
// Messaggi di errore import
|
||||
["msg.xkey.required"] = ["Incolla una chiave estesa (xpub/xprv o variante).", "Paste an extended key (xpub/xprv or variant).", "Pega una clave extendida (xpub/xprv o variante).", "Collez une clé étendue (xpub/xprv ou variante).", "Cole uma chave estendida (xpub/xprv ou variante).", "Fügen Sie einen erweiterten Schlüssel ein (xpub/xprv oder Variante)."],
|
||||
["msg.xkey.invalid"] = ["Chiave estesa non riconosciuta per questa rete.", "Extended key not recognised for this network.", "Clave extendida no reconocida para esta red.", "Clé étendue non reconnue pour ce réseau.", "Chave estendida não reconhecida para esta rede.", "Erweiterter Schlüssel für dieses Netzwerk nicht erkannt."],
|
||||
["msg.wif.required"] = ["Incolla almeno una chiave WIF.", "Paste at least one WIF key.", "Pega al menos una clave WIF.", "Collez au moins une clé WIF.", "Cole pelo menos uma chave WIF.", "Fügen Sie mindestens einen WIF-Schlüssel ein."],
|
||||
["msg.wif.invalid"] = ["Chiave WIF non valida per questa rete.", "WIF key not valid for this network.", "Clave WIF no válida para esta red.", "Clé WIF invalide pour ce réseau.", "Chave WIF inválida para esta rede.", "WIF-Schlüssel für dieses Netzwerk ungültig."],
|
||||
|
||||
// Pannello wallet
|
||||
["wallet.close"] = ["Chiudi wallet", "Close wallet", "Cerrar wallet", "Fermer le wallet", "Fechar carteira", "Wallet schließen"],
|
||||
|
||||
@@ -186,7 +186,7 @@ public partial class MainWindowViewModel
|
||||
Addresses.Add(new AddressRow(_loc["addr.receive"], i,
|
||||
_account.GetReceiveAddress(i).ToString(), "—", "—",
|
||||
false,
|
||||
_account.GetPublicKey(false, i).ToHex(),
|
||||
_account.GetPublicKey(false, i)?.ToHex() ?? "",
|
||||
KeyWif(false, i),
|
||||
$"m/{_doc!.AccountPath}/0/{i}"));
|
||||
return;
|
||||
@@ -214,7 +214,7 @@ public partial class MainWindowViewModel
|
||||
a.BalanceSats > 0 ? Fmt(a.BalanceSats, withLabel: false) : (a.TxCount > 0 ? "0" : "—"),
|
||||
a.TxCount > 0 ? a.TxCount.ToString() : "—",
|
||||
a.IsChange,
|
||||
_account.GetPublicKey(a.IsChange, a.Index).ToHex(),
|
||||
_account.GetPublicKey(a.IsChange, a.Index)?.ToHex() ?? "",
|
||||
KeyWif(a.IsChange, a.Index),
|
||||
$"m/{_doc!.AccountPath}/{(a.IsChange ? 1 : 0)}/{a.Index}"));
|
||||
}
|
||||
|
||||
@@ -24,8 +24,13 @@ public partial class MainWindowViewModel
|
||||
public const string StepWords = "words";
|
||||
public const string StepPassphrase = "passphrase";
|
||||
public const string StepScriptType = "script-type";
|
||||
public const string StepImportXkey = "import-xkey";
|
||||
public const string StepImportWif = "import-wif";
|
||||
public const string StepPassword = "password";
|
||||
|
||||
private enum WizardFlowKind { New, Restore, ImportXkey, ImportWif }
|
||||
private WizardFlowKind _wizardFlow;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(IsStepDataLocation))]
|
||||
[NotifyPropertyChangedFor(nameof(IsStepStart))]
|
||||
@@ -36,6 +41,8 @@ public partial class MainWindowViewModel
|
||||
[NotifyPropertyChangedFor(nameof(IsStepWords))]
|
||||
[NotifyPropertyChangedFor(nameof(IsStepPassphrase))]
|
||||
[NotifyPropertyChangedFor(nameof(IsStepScriptType))]
|
||||
[NotifyPropertyChangedFor(nameof(IsStepImportXkey))]
|
||||
[NotifyPropertyChangedFor(nameof(IsStepImportWif))]
|
||||
[NotifyPropertyChangedFor(nameof(IsStepPassword))]
|
||||
private string setupStep = StepStart;
|
||||
|
||||
@@ -48,6 +55,8 @@ public partial class MainWindowViewModel
|
||||
public bool IsStepWords => SetupStep == StepWords;
|
||||
public bool IsStepPassphrase => SetupStep == StepPassphrase;
|
||||
public bool IsStepScriptType => SetupStep == StepScriptType;
|
||||
public bool IsStepImportXkey => SetupStep == StepImportXkey;
|
||||
public bool IsStepImportWif => SetupStep == StepImportWif;
|
||||
public bool IsStepPassword => SetupStep == StepPassword;
|
||||
|
||||
[ObservableProperty]
|
||||
@@ -69,6 +78,16 @@ public partial class MainWindowViewModel
|
||||
|
||||
public string DefaultDataPath => AppPaths.DefaultDataRoot();
|
||||
|
||||
[ObservableProperty]
|
||||
private string importXkeyInput = "";
|
||||
|
||||
[ObservableProperty]
|
||||
private string importWifInput = "";
|
||||
|
||||
// Tipo di script rilevato durante la decodifica dell'xkey (per mostrarlo all'utente)
|
||||
[ObservableProperty]
|
||||
private string importXkeyDetectedKind = "";
|
||||
|
||||
[ObservableProperty]
|
||||
private string mnemonicInput = "";
|
||||
|
||||
@@ -157,7 +176,7 @@ public partial class MainWindowViewModel
|
||||
[RelayCommand]
|
||||
private void WizardStartNew()
|
||||
{
|
||||
_isRestoreFlow = false;
|
||||
_wizardFlow = WizardFlowKind.New;
|
||||
MnemonicInput = Bip39.Generate(MnemonicLength.Twelve).ToString();
|
||||
SetupStep = StepShowSeed;
|
||||
StatusMessage = "";
|
||||
@@ -166,12 +185,31 @@ public partial class MainWindowViewModel
|
||||
[RelayCommand]
|
||||
private void WizardStartRestore()
|
||||
{
|
||||
_isRestoreFlow = true;
|
||||
_wizardFlow = WizardFlowKind.Restore;
|
||||
MnemonicInput = "";
|
||||
SetupStep = StepWords;
|
||||
StatusMessage = "";
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void WizardStartImportXkey()
|
||||
{
|
||||
_wizardFlow = WizardFlowKind.ImportXkey;
|
||||
ImportXkeyInput = "";
|
||||
ImportXkeyDetectedKind = "";
|
||||
SetupStep = StepImportXkey;
|
||||
StatusMessage = "";
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void WizardStartImportWif()
|
||||
{
|
||||
_wizardFlow = WizardFlowKind.ImportWif;
|
||||
ImportWifInput = "";
|
||||
SetupStep = StepImportWif;
|
||||
StatusMessage = "";
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void WizardNextFromShowSeed()
|
||||
{
|
||||
@@ -218,6 +256,58 @@ public partial class MainWindowViewModel
|
||||
StatusMessage = "";
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void WizardNextFromImportXkey()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(ImportXkeyInput))
|
||||
{
|
||||
StatusMessage = Loc.Tr("msg.xkey.required");
|
||||
return;
|
||||
}
|
||||
// Valida e rileva il tipo: prova prima come xpub, poi come xprv.
|
||||
if (Slip132.TryDecodePublic(ImportXkeyInput.Trim(), Profile, out _, out var pubKind))
|
||||
{
|
||||
SelectedScriptKind = pubKind;
|
||||
ImportXkeyDetectedKind = $"{pubKind} (watch-only)";
|
||||
}
|
||||
else if (Slip132.TryDecodePrivate(ImportXkeyInput.Trim(), Profile, out _, out var prvKind))
|
||||
{
|
||||
SelectedScriptKind = prvKind;
|
||||
ImportXkeyDetectedKind = prvKind.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
StatusMessage = Loc.Tr("msg.xkey.invalid");
|
||||
return;
|
||||
}
|
||||
PasswordInput = ConfirmPasswordInput = "";
|
||||
EncryptWallet = true;
|
||||
SetupStep = StepPassword;
|
||||
StatusMessage = "";
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void WizardNextFromImportWif()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(ImportWifInput))
|
||||
{
|
||||
StatusMessage = Loc.Tr("msg.wif.required");
|
||||
return;
|
||||
}
|
||||
// Valida il WIF con un parsing anticipato.
|
||||
try
|
||||
{
|
||||
_ = new NBitcoin.BitcoinSecret(ImportWifInput.Trim(), PalladiumNetworks.For(Net));
|
||||
}
|
||||
catch
|
||||
{
|
||||
StatusMessage = Loc.Tr("msg.wif.invalid");
|
||||
return;
|
||||
}
|
||||
SetupStep = StepScriptType;
|
||||
StatusMessage = "";
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void WizardNextFromScriptType()
|
||||
{
|
||||
@@ -233,11 +323,11 @@ public partial class MainWindowViewModel
|
||||
SetupStep = SetupStep switch
|
||||
{
|
||||
StepOpen => WalletList.Count > 1 ? StepChooseWallet : StepStart,
|
||||
StepChooseWallet or StepShowSeed or StepWords => StepStart,
|
||||
StepChooseWallet or StepShowSeed or StepWords or StepImportXkey or StepImportWif => StepStart,
|
||||
StepConfirmSeed => StepShowSeed,
|
||||
StepPassphrase => _isRestoreFlow ? StepWords : StepConfirmSeed,
|
||||
StepScriptType => StepPassphrase,
|
||||
StepPassword => StepScriptType,
|
||||
StepPassphrase => _wizardFlow == WizardFlowKind.Restore ? StepWords : StepConfirmSeed,
|
||||
StepScriptType => _wizardFlow == WizardFlowKind.ImportWif ? StepImportWif : StepPassphrase,
|
||||
StepPassword => _wizardFlow == WizardFlowKind.ImportXkey ? StepImportXkey : StepScriptType,
|
||||
_ => StepStart,
|
||||
};
|
||||
if (SetupStep == StepStart)
|
||||
@@ -269,11 +359,46 @@ public partial class MainWindowViewModel
|
||||
|
||||
try
|
||||
{
|
||||
var (doc, account) = WalletLoader.NewFromMnemonic(
|
||||
WalletDocument doc;
|
||||
IWalletAccount account;
|
||||
|
||||
switch (_wizardFlow)
|
||||
{
|
||||
case WizardFlowKind.ImportXkey:
|
||||
{
|
||||
var input = ImportXkeyInput.Trim();
|
||||
if (Slip132.TryDecodePublic(input, Profile, out _, out _))
|
||||
{
|
||||
var (d, a) = WalletLoader.NewFromXpub(input, Profile, SelectedScriptKind);
|
||||
(doc, account) = (d, a);
|
||||
}
|
||||
else
|
||||
{
|
||||
var (d, a) = WalletLoader.NewFromXprv(input, Profile, SelectedScriptKind);
|
||||
(doc, account) = (d, a);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WizardFlowKind.ImportWif:
|
||||
{
|
||||
var wifLines = ImportWifInput.Split('\n',
|
||||
StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||
var (d, a) = WalletLoader.NewFromWif(wifLines, SelectedScriptKind, Profile);
|
||||
(doc, account) = (d, a);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
var (d, a) = WalletLoader.NewFromMnemonic(
|
||||
MnemonicInput,
|
||||
string.IsNullOrEmpty(PassphraseInput) ? null : PassphraseInput,
|
||||
SelectedScriptKind,
|
||||
Profile);
|
||||
(doc, account) = (d, a);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var path = AppPaths.DefaultWalletPath(Net);
|
||||
for (var n = 2; WalletStore.Exists(path); n++)
|
||||
path = Path.Combine(AppPaths.WalletsDir(Net), $"wallet-{n}.wallet.json");
|
||||
@@ -365,7 +490,7 @@ public partial class MainWindowViewModel
|
||||
}
|
||||
|
||||
private void OpenLoaded(
|
||||
WalletDocument doc, HdAccount account, string path, string? password, WalletLock walletLock)
|
||||
WalletDocument doc, IWalletAccount account, string path, string? password, WalletLock walletLock)
|
||||
{
|
||||
_walletLock?.Dispose();
|
||||
_walletLock = walletLock;
|
||||
@@ -375,14 +500,23 @@ public partial class MainWindowViewModel
|
||||
_walletPath = path;
|
||||
_password = password;
|
||||
MnemonicInput = ConfirmMnemonicInput = PassphraseInput = PasswordInput = ConfirmPasswordInput = "";
|
||||
ImportXkeyInput = ImportWifInput = ImportXkeyDetectedKind = "";
|
||||
SetupStep = StepStart;
|
||||
|
||||
NetworkInfo = $"{doc.Network} · {doc.ScriptKind} · m/{doc.AccountPath}"
|
||||
+ (doc.IsWatchOnly ? " · watch-only" : "");
|
||||
var walletKindTag = account switch
|
||||
{
|
||||
ImportedKeyAccount => " · imported",
|
||||
{ IsWatchOnly: true } => " · watch-only",
|
||||
_ => ""
|
||||
};
|
||||
var pathTag = !string.IsNullOrEmpty(doc.AccountPath) ? $" · m/{doc.AccountPath}" : "";
|
||||
NetworkInfo = $"{doc.Network} · {doc.ScriptKind}{pathTag}{walletKindTag}";
|
||||
LoadContacts();
|
||||
ApplyCache(doc.Cache);
|
||||
IsWalletOpen = true;
|
||||
StatusMessage = Loc.Tr("msg.opened");
|
||||
_autoReconnect = true; // keepalive riprova la connessione anche se il primo tentativo fallisce
|
||||
_syncFailed = true; // forza la prima sync automatica
|
||||
_ = ConnectAndSync();
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,7 @@ public partial class MainWindowViewModel : ViewModelBase
|
||||
{
|
||||
// ---- stato sessione wallet ----
|
||||
private WalletDocument? _doc;
|
||||
private HdAccount? _account;
|
||||
private IWalletAccount? _account;
|
||||
private string? _walletPath;
|
||||
private string? _password;
|
||||
private WalletLock? _walletLock;
|
||||
@@ -73,10 +73,10 @@ public partial class MainWindowViewModel : ViewModelBase
|
||||
|
||||
// ---- wizard ----
|
||||
private string? _pendingOpenPath;
|
||||
private bool _isRestoreFlow;
|
||||
|
||||
// ---- keep-alive ----
|
||||
private bool _autoReconnect;
|
||||
private bool _syncFailed;
|
||||
private readonly DispatcherTimer _keepAliveTimer;
|
||||
|
||||
// ---- server UI sync ----
|
||||
@@ -135,8 +135,8 @@ public partial class MainWindowViewModel : ViewModelBase
|
||||
if (_account is null or { IsWatchOnly: true }) return "";
|
||||
try
|
||||
{
|
||||
return _account.GetExtPrivateKey(isChange, index)
|
||||
.PrivateKey.GetWif(PalladiumNetworks.For(Net)).ToString();
|
||||
return _account.GetPrivateKey(isChange, index)
|
||||
?.GetWif(PalladiumNetworks.For(Net)).ToString() ?? "";
|
||||
}
|
||||
catch { return ""; }
|
||||
}
|
||||
@@ -161,6 +161,12 @@ public partial class MainWindowViewModel : ViewModelBase
|
||||
return;
|
||||
if (_client is { IsConnected: true })
|
||||
{
|
||||
// Se il wallet è aperto e l'ultima sync è fallita, riprova automaticamente.
|
||||
if (_syncFailed && _account is not null)
|
||||
{
|
||||
await ConnectAndSync();
|
||||
return;
|
||||
}
|
||||
try { await _client.PingAsync(); }
|
||||
catch { }
|
||||
}
|
||||
@@ -183,6 +189,7 @@ public partial class MainWindowViewModel : ViewModelBase
|
||||
_synchronizer = null;
|
||||
_autoReconnect = false;
|
||||
_resyncRequested = false;
|
||||
_syncFailed = false;
|
||||
_doc = null;
|
||||
_account = null;
|
||||
_lastTransactions = null;
|
||||
|
||||
@@ -87,6 +87,12 @@
|
||||
<Button Content="{Binding Loc[wiz.restore.btn]}" FontSize="16"
|
||||
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"
|
||||
Command="{Binding WizardStartRestoreCommand}"/>
|
||||
<Button Content="{Binding Loc[wiz.importxkey.btn]}" FontSize="16"
|
||||
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"
|
||||
Command="{Binding WizardStartImportXkeyCommand}"/>
|
||||
<Button Content="{Binding Loc[wiz.importwif.btn]}" FontSize="16"
|
||||
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"
|
||||
Command="{Binding WizardStartImportWifCommand}"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Passo: scelta del wallet (più file presenti) -->
|
||||
@@ -171,6 +177,35 @@
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Passo: import xpub/xprv estesa (SLIP-132) -->
|
||||
<StackPanel IsVisible="{Binding IsStepImportXkey}" Spacing="12">
|
||||
<TextBlock Text="{Binding Loc[wiz.importxkey.title]}" FontSize="18" FontWeight="Bold"/>
|
||||
<TextBlock Text="{Binding Loc[wiz.importxkey.hint]}" TextWrapping="Wrap" Foreground="Gray"/>
|
||||
<TextBox PlaceholderText="{Binding Loc[wiz.importxkey.placeholder]}"
|
||||
Text="{Binding ImportXkeyInput}" AcceptsReturn="False"/>
|
||||
<TextBlock Text="{Binding ImportXkeyDetectedKind}"
|
||||
IsVisible="{Binding ImportXkeyDetectedKind, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"
|
||||
Foreground="Green" FontSize="12"/>
|
||||
<StackPanel Orientation="Horizontal" Spacing="10">
|
||||
<Button Content="{Binding Loc[wiz.back]}" Command="{Binding WizardBackCommand}"/>
|
||||
<Button Content="{Binding Loc[wiz.next]}" Classes="accent"
|
||||
Command="{Binding WizardNextFromImportXkeyCommand}"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Passo: import chiave WIF singola -->
|
||||
<StackPanel IsVisible="{Binding IsStepImportWif}" Spacing="12">
|
||||
<TextBlock Text="{Binding Loc[wiz.importwif.title]}" FontSize="18" FontWeight="Bold"/>
|
||||
<TextBlock Text="{Binding Loc[wiz.importwif.hint]}" TextWrapping="Wrap" Foreground="Gray"/>
|
||||
<TextBox PlaceholderText="{Binding Loc[wiz.importwif.placeholder]}"
|
||||
Text="{Binding ImportWifInput}" AcceptsReturn="True" Height="80"/>
|
||||
<StackPanel Orientation="Horizontal" Spacing="10">
|
||||
<Button Content="{Binding Loc[wiz.back]}" Command="{Binding WizardBackCommand}"/>
|
||||
<Button Content="{Binding Loc[wiz.next]}" Classes="accent"
|
||||
Command="{Binding WizardNextFromImportWifCommand}"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Passo: scelta del tipo di script/indirizzi -->
|
||||
<StackPanel IsVisible="{Binding IsStepScriptType}" Spacing="12">
|
||||
<TextBlock Text="{Binding Loc[wiz.scripttype.title]}" FontSize="18" FontWeight="Bold"/>
|
||||
|
||||
Reference in New Issue
Block a user