diff --git a/liana-gui/src/app/error.rs b/liana-gui/src/app/error.rs index 4050ed88..4c5cee82 100644 --- a/liana-gui/src/app/error.rs +++ b/liana-gui/src/app/error.rs @@ -7,6 +7,7 @@ use lianad::config::ConfigError; use crate::{ app::{settings::SettingsError, wallet::WalletError}, daemon::DaemonError, + export, }; #[derive(Debug)] @@ -18,6 +19,7 @@ pub enum Error { HardwareWallet(async_hwi::Error), Desc(LianaDescError), Spend(SpendCreationError), + ImportExport(export::Error), } impl std::fmt::Display for Error { @@ -58,6 +60,7 @@ impl std::fmt::Display for Error { Self::Unexpected(e) => write!(f, "Unexpected error: {}", e), Self::HardwareWallet(e) => write!(f, "error: {}\nPlease check if the device is still connected and unlocked with the correct firmware open for the current network and no other application is accessing the device.", e), Self::Desc(e) => write!(f, "Liana descriptor error: {}", e), + Self::ImportExport(e) => write!(f, "{e:?}"), } } } diff --git a/liana-gui/src/app/state/export.rs b/liana-gui/src/app/state/export.rs index 1e413705..7de48c93 100644 --- a/liana-gui/src/app/state/export.rs +++ b/liana-gui/src/app/state/export.rs @@ -127,6 +127,11 @@ impl ExportModal { } // TODO: forward Descriptor } + Progress::UpdateAliases(map) => { + return Task::perform(async {}, move |_| { + ImportExportMessage::UpdateAliases(map.clone()).into() + }); + } }, ImportExportMessage::TimedOut => { self.stop(ImportExportState::TimedOut); @@ -173,6 +178,7 @@ impl ExportModal { } } } + ImportExportMessage::UpdateAliases(_) => { /* unexpected */ } } Task::none() } diff --git a/liana-gui/src/app/state/settings/mod.rs b/liana-gui/src/app/state/settings/mod.rs index d1bc6a0a..4e73a94c 100644 --- a/liana-gui/src/app/state/settings/mod.rs +++ b/liana-gui/src/app/state/settings/mod.rs @@ -10,7 +10,7 @@ use iced::Task; use liana_ui::{component::form, widget::Element}; use bitcoind::BitcoindSettingsState; -use wallet::WalletSettingsState; +use wallet::{app_backup, WalletSettingsState}; use crate::{ app::{ @@ -22,9 +22,8 @@ use crate::{ wallet::Wallet, Config, }, - backup::Backup, daemon::{Daemon, DaemonBackend}, - export::{ImportExportMessage, ImportExportType}, + export::{self, ImportExportMessage, ImportExportType}, }; use super::export::ExportModal; @@ -224,6 +223,14 @@ impl State for ImportExportSettingsState { self.modal = None; } Message::View(view::Message::ImportExport(m)) => { + if let ImportExportMessage::UpdateAliases(aliases) = m { + let mut wallet = (*self.wallet).clone(); + wallet.keys_aliases = aliases; + let wallet = Arc::new(wallet); + return Task::perform(async {}, move |_| { + Message::WalletUpdated(Ok(wallet.clone())) + }); + } if let Some(modal) = self.modal.as_mut() { return modal.update(m); }; @@ -262,29 +269,32 @@ impl State for ImportExportSettingsState { let wallet = self.wallet.clone(); let daemon = daemon.clone(); return Task::perform( - async move { - let backup = - Backup::from_app(datadir, network, config, wallet, daemon).await; - let backup = backup.unwrap(); - serde_json::to_string_pretty(&backup).unwrap() - // TODO: do not unwrap, return an error message instead - }, + async move { app_backup(datadir, network, config, wallet, daemon).await }, |s| { Message::View(view::Message::Settings( - view::SettingsMessage::ExportBackup(Ok(s)), + view::SettingsMessage::ExportBackup(s), )) }, ); } } - Message::View(view::Message::Settings(view::SettingsMessage::ExportBackup(Ok( - backup, - )))) => { + Message::View(view::Message::Settings(view::SettingsMessage::ExportBackup(backup))) => { + let backup = match backup { + Ok(b) => b, + Err(e) => { + self.warning = Some(Error::ImportExport(export::Error::Backup(e))); + return Task::none(); + } + }; let modal = ExportModal::new(Some(daemon), ImportExportType::ExportBackup(backup)); launch!(self, modal, true); } Message::View(view::Message::Settings(view::SettingsMessage::ImportWallet)) => { - // TODO: + if self.modal.is_none() { + let modal = + ExportModal::new(Some(daemon), ImportExportType::ImportBackup(None, None)); + launch!(self, modal, false); + } } _ => {} } diff --git a/liana-gui/src/app/state/settings/wallet.rs b/liana-gui/src/app/state/settings/wallet.rs index dd06a9c5..8b52f650 100644 --- a/liana-gui/src/app/state/settings/wallet.rs +++ b/liana-gui/src/app/state/settings/wallet.rs @@ -26,9 +26,9 @@ use crate::{ wallet::Wallet, Config, }, - backup::Backup, + backup::{self, Backup}, daemon::{Daemon, DaemonBackend}, - export::{ImportExportMessage, ImportExportType}, + export::{self, ImportExportMessage, ImportExportType}, hw::{HardwareWallet, HardwareWalletConfig, HardwareWallets}, }; @@ -231,16 +231,10 @@ impl State for WalletSettingsState { let wallet = self.wallet.clone(); let daemon = daemon.clone(); Task::perform( - async move { - let backup = - Backup::from_app(datadir, network, config, wallet, daemon).await; - let backup = backup.unwrap(); - serde_json::to_string_pretty(&backup).unwrap() - // TODO: do not unwrap, return an error message instead - }, + async move { app_backup(datadir, network, config, wallet, daemon).await }, |s| { Message::View(view::Message::Settings( - view::SettingsMessage::ExportBackup(Ok(s)), + view::SettingsMessage::ExportBackup(s), )) }, ) @@ -248,10 +242,15 @@ impl State for WalletSettingsState { Task::none() } } - Message::View(view::Message::Settings(view::SettingsMessage::ExportBackup(Ok( - backup, - )))) => { + Message::View(view::Message::Settings(view::SettingsMessage::ExportBackup(backup))) => { if self.modal.is_none() { + let backup = match backup { + Ok(b) => b, + Err(e) => { + self.warning = Some(Error::ImportExport(export::Error::Backup(e))); + return Task::none(); + } + }; let modal = ExportModal::new(Some(daemon), ImportExportType::ExportBackup(backup)); let launch = modal.launch(true); @@ -512,3 +511,14 @@ async fn update_keys_aliases( Ok(Arc::new(wallet)) } + +pub async fn app_backup( + datadir: PathBuf, + network: Network, + config: Arc, + wallet: Arc, + daemon: Arc, +) -> Result { + let backup = Backup::from_app(datadir, network, config, wallet, daemon).await?; + serde_json::to_string_pretty(&backup).map_err(|_| backup::Error::Json) +} diff --git a/liana-gui/src/app/view/warning.rs b/liana-gui/src/app/view/warning.rs index cdc18bd8..4af3fabe 100644 --- a/liana-gui/src/app/view/warning.rs +++ b/liana-gui/src/app/view/warning.rs @@ -49,6 +49,7 @@ impl From<&Error> for WarningMessage { Error::HardwareWallet(_) => WarningMessage("Hardware wallet error".to_string()), Error::Desc(e) => WarningMessage(format!("Descriptor analysis error: '{}'.", e)), Error::Spend(e) => WarningMessage(format!("Spend creation error: '{}'.", e)), + Error::ImportExport(e) => WarningMessage(format!("{e:?}")), } } } diff --git a/liana-gui/src/export.rs b/liana-gui/src/export.rs index fb797416..7bdf7e8d 100644 --- a/liana-gui/src/export.rs +++ b/liana-gui/src/export.rs @@ -11,6 +11,7 @@ use std::{ time, }; +use async_hwi::bitbox::api::btc::Fingerprint; use chrono::{DateTime, Duration, Utc}; use liana::{ descriptors::LianaDescriptor, @@ -32,7 +33,7 @@ use crate::{ settings::{KeySetting, Settings}, view, }, - backup::Backup, + backup::{self, Backup}, daemon::{ model::{HistoryTransaction, Labelled}, Daemon, DaemonBackend, DaemonError, @@ -115,6 +116,7 @@ pub enum ImportExportMessage { Close, Overwrite, Ignore, + UpdateAliases(HashMap), } impl From for view::Message { @@ -151,6 +153,7 @@ pub enum Error { ParseDescriptor, Bip329Export(String), BackupImport(String), + Backup(backup::Error), } #[derive(Debug, Clone)] @@ -240,6 +243,7 @@ pub enum Progress { Descriptor(LianaDescriptor), LabelsConflict(SyncSender), KeyAliasesConflict(SyncSender), + UpdateAliases(HashMap), } pub struct Export {