diff --git a/liana-gui/src/export.rs b/liana-gui/src/export.rs index 91479803..1234825c 100644 --- a/liana-gui/src/export.rs +++ b/liana-gui/src/export.rs @@ -191,11 +191,11 @@ pub enum ImportExportType { Option>, /*overwrite_labels*/ Option>, /*overwrite_aliases*/ ), + WalletFromBackup, Descriptor(LianaDescriptor), ExportLabels, ImportPsbt, ImportDescriptor, - WalletFromBackup, } impl ImportExportType { @@ -276,7 +276,7 @@ pub enum Progress { ( LianaDescriptor, Network, - HashMap, + HashMap, Backup, ), ), @@ -315,7 +315,6 @@ impl Export { path: PathBuf, ) { match export_type { - ImportExportType::WalletFromBackup => wallet_from_backup(sender, path).await, ImportExportType::Transactions => export_transactions(sender, daemon, path).await, ImportExportType::ExportPsbt(str) => export_string(sender, path, str), ImportExportType::Descriptor(descriptor) => export_descriptor(sender, path, descriptor), @@ -324,6 +323,7 @@ impl Export { ImportExportType::ImportDescriptor => import_descriptor(sender, path), ImportExportType::ExportBackup(str) => export_string(sender, path, str), ImportExportType::ImportBackup(..) => import_backup(sender, path, daemon).await, + ImportExportType::WalletFromBackup => wallet_from_backup(sender, path).await, }; } @@ -1092,10 +1092,16 @@ pub async fn wallet_from_backup(sender: Sender, path: PathBuf) { } }; - let mut aliases: HashMap = HashMap::new(); + let mut aliases: HashMap = HashMap::new(); for (k, v) in &account.keys { - if let Some(alias) = &v.alias { - aliases.insert(*k, alias.clone()); + if let Some(ks) = KeySetting::from_backup( + v.alias.clone().unwrap_or("".into()), + *k, + v.role, + v.key_type, + v.metadata.clone(), + ) { + aliases.insert(*k, ks); } } diff --git a/liana-gui/src/installer/message.rs b/liana-gui/src/installer/message.rs index 81ebc225..efe5dc2f 100644 --- a/liana-gui/src/installer/message.rs +++ b/liana-gui/src/installer/message.rs @@ -2,13 +2,15 @@ use liana::miniscript::{ bitcoin::{bip32::Fingerprint, Network}, DescriptorPublicKey, }; -use std::path::PathBuf; +use std::{collections::HashMap, path::PathBuf}; use super::{context, Error}; use crate::{ - app::settings::ProviderKey, - app::view::Close, - backup, + app::{ + settings::{self, ProviderKey}, + view::Close, + }, + backup::{self, Backup}, download::{DownloadError, Progress}, export::ImportExportMessage, hw::HardwareWalletMessage, @@ -55,6 +57,8 @@ pub enum Message { BackupWallet, ExportWallet(Result), ImportExport(ImportExportMessage), + ImportBackup, + WalletFromBackup((HashMap, Backup)), } impl Close for Message { diff --git a/liana-gui/src/installer/mod.rs b/liana-gui/src/installer/mod.rs index 2eee9ed2..245121cf 100644 --- a/liana-gui/src/installer/mod.rs +++ b/liana-gui/src/installer/mod.rs @@ -292,6 +292,11 @@ impl Installer { .expect("There is always a step") .update(&mut self.hws, Message::Installed(Err(e))) } + Message::WalletFromBackup((ks, backup)) => { + self.context.keys = ks; + self.context.backup = Some(backup); + Task::none() + } _ => self .steps .get_mut(self.current) diff --git a/liana-gui/src/installer/step/descriptor/mod.rs b/liana-gui/src/installer/step/descriptor/mod.rs index c1dcd91e..866fb447 100644 --- a/liana-gui/src/installer/step/descriptor/mod.rs +++ b/liana-gui/src/installer/step/descriptor/mod.rs @@ -18,7 +18,7 @@ use async_hwi::DeviceKind; use crate::{ app::{settings::KeySetting, state::export::ExportModal, wallet::wallet_name}, backup::{self, Backup}, - export::{ImportExportMessage, ImportExportType}, + export::{ImportExportMessage, ImportExportType, Progress}, hw::{HardwareWallet, HardwareWallets}, installer::{ message::{self, Message}, @@ -32,6 +32,8 @@ pub struct ImportDescriptor { imported_descriptor: form::Value, wrong_network: bool, error: Option, + modal: Option, + imported_backup: bool, } impl ImportDescriptor { @@ -41,6 +43,8 @@ impl ImportDescriptor { imported_descriptor: form::Value::default(), wrong_network: false, error: None, + modal: None, + imported_backup: false, } } @@ -77,14 +81,55 @@ impl Step for ImportDescriptor { fn skip(&self, ctx: &Context) -> bool { ctx.remote_backend.is_some() } - // form value is set as valid each time it is edited. - // Verification of the values is happening when the user click on Next button. + + fn subscription(&self, _hws: &HardwareWallets) -> Subscription { + if let Some(modal) = &self.modal { + if let Some(sub) = modal.subscription() { + sub.map(|m| Message::ImportExport(ImportExportMessage::Progress(m))) + } else { + Subscription::none() + } + } else { + Subscription::none() + } + } + fn update(&mut self, _hws: &mut HardwareWallets, message: Message) -> Task { - if let Message::DefineDescriptor(message::DefineDescriptor::ImportDescriptor(desc)) = - message - { - self.imported_descriptor.value = desc; - self.check_descriptor(self.network); + match message { + Message::DefineDescriptor(message::DefineDescriptor::ImportDescriptor(desc)) => { + self.imported_descriptor.value = desc; + self.check_descriptor(self.network); + } + Message::ImportExport(ImportExportMessage::Close) => { + self.modal = None; + } + Message::ImportBackup => { + if !self.imported_backup { + let modal = ExportModal::new(None, ImportExportType::WalletFromBackup); + let launch = modal.launch(false); + self.modal = Some(modal); + return launch; + } + } + Message::ImportExport(ImportExportMessage::Progress(Progress::WalletFromBackup(r))) => { + let (descriptor, network, aliases, backup) = r; + if self.network == network { + self.imported_backup = true; + self.imported_descriptor.value = descriptor.to_string(); + return Task::perform(async move { (aliases, backup) }, |(a, b)| { + Message::WalletFromBackup((a, b)) + }); + } else { + self.error = Some("Backup network do not match the selected network!".into()); + } + } + Message::ImportExport(m) => { + if let Some(modal) = self.modal.as_mut() { + let task: Task = modal.update(m); + return task; + }; + } + _ => {} } Task::none() } @@ -108,13 +153,19 @@ impl Step for ImportDescriptor { progress: (usize, usize), email: Option<&'a str>, ) -> Element { - view::import_descriptor( + let content = view::import_descriptor( progress, email, &self.imported_descriptor, + self.imported_backup, self.wrong_network, self.error.as_ref(), - ) + ); + if let Some(modal) = &self.modal { + modal.view(content) + } else { + content + } } } diff --git a/liana-gui/src/installer/view/mod.rs b/liana-gui/src/installer/view/mod.rs index 733df5b9..ba405730 100644 --- a/liana-gui/src/installer/view/mod.rs +++ b/liana-gui/src/installer/view/mod.rs @@ -264,11 +264,15 @@ pub fn import_descriptor<'a>( progress: (usize, usize), email: Option<&'a str>, imported_descriptor: &form::Value, + imported_backup: bool, wrong_network: bool, error: Option<&String>, ) -> Element<'a, Message> { + let valid = !imported_descriptor.value.is_empty() && imported_descriptor.valid; + let col_descriptor = Column::new() .push(text("Descriptor:").bold()) + .push(Space::with_height(10)) .push( form::Form::new_trimmed("Descriptor", imported_descriptor, |msg| { Message::DefineDescriptor(message::DefineDescriptor::ImportDescriptor(msg)) @@ -280,21 +284,65 @@ pub fn import_descriptor<'a>( }) .size(text::P1_SIZE) .padding(10), + ); + + let descriptor = if imported_backup { + None + } else { + Some(col_descriptor) + }; + + let or = if !valid && !imported_backup { + Some( + Row::new() + .push(text("or").bold()) + .push(Space::with_width(Length::Fill)), ) - .spacing(10); + } else { + None + }; + + let import_backup = if !valid && !imported_backup { + Some( + Row::new() + .push(button::secondary(None, "Import backup").on_press(Message::ImportBackup)) + .push(Space::with_width(Length::Fill)), + ) + } else { + None + }; + + let backup_imported = if imported_backup { + Some( + Row::new() + .push(text("Backup successfuly imported!").bold()) + .push(Space::with_width(Length::Fill)), + ) + } else { + None + }; + layout( progress, email, "Import the wallet", Column::new() - .push(Column::new().spacing(20).push(col_descriptor).push(text( - "If you are using a Bitcoin Core node, \ + .push( + Column::new() + .spacing(20) + .push_maybe(descriptor) + .push_maybe(or) + .push_maybe(import_backup) + .push_maybe(backup_imported) + .push(text( + "If you are using a Bitcoin Core node, \ you will need to perform a rescan of \ the blockchain after creating the wallet \ in order to see your coins and past \ transactions. This can be done in \ Settings > Node.", - ))) + )), + ) .push( if imported_descriptor.value.is_empty() || !imported_descriptor.valid { button::secondary(None, "Next").width(Length::Fixed(200.0))