Skip to content

Commit db65943

Browse files
committed
installer: add wallet export feature at descriptor backup step
1 parent 8a6eef0 commit db65943

File tree

9 files changed

+108
-24
lines changed

9 files changed

+108
-24
lines changed

liana-gui/src/app/state/export.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,12 @@ impl ExportModal {
9595
self.state = ImportExportState::Progress(p);
9696
}
9797
}
98-
Progress::Finished | Progress::Ended => self.state = ImportExportState::Ended,
99-
Progress::Error(e) => self.error = Some(e),
98+
Progress::Finished | Progress::Ended => {
99+
self.state = ImportExportState::Ended;
100+
}
101+
Progress::Error(e) => {
102+
self.error = Some(e);
103+
}
100104
Progress::None => {}
101105
Progress::Psbt(_) => {
102106
if self.import_export_type == ImportExportType::ImportPsbt {

liana-gui/src/app/state/settings/mod.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -271,13 +271,15 @@ impl State for ImportExportSettingsState {
271271
},
272272
|s| {
273273
Message::View(view::Message::Settings(
274-
view::SettingsMessage::ExportBackup(s),
274+
view::SettingsMessage::ExportBackup(Ok(s)),
275275
))
276276
},
277277
);
278278
}
279279
}
280-
Message::View(view::Message::Settings(view::SettingsMessage::ExportBackup(backup))) => {
280+
Message::View(view::Message::Settings(view::SettingsMessage::ExportBackup(Ok(
281+
backup,
282+
)))) => {
281283
let modal = ExportModal::new(Some(daemon), ImportExportType::ExportBackup(backup));
282284
launch!(self, modal);
283285
}

liana-gui/src/app/state/settings/wallet.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -239,15 +239,17 @@ impl State for WalletSettingsState {
239239
},
240240
|s| {
241241
Message::View(view::Message::Settings(
242-
view::SettingsMessage::ExportBackup(s),
242+
view::SettingsMessage::ExportBackup(Ok(s)),
243243
))
244244
},
245245
)
246246
} else {
247247
Task::none()
248248
}
249249
}
250-
Message::View(view::Message::Settings(view::SettingsMessage::ExportBackup(backup))) => {
250+
Message::View(view::Message::Settings(view::SettingsMessage::ExportBackup(Ok(
251+
backup,
252+
)))) => {
251253
if self.modal.is_none() {
252254
let modal =
253255
// ExportModal::new(Some(daemon), ImportExportType::ExportBackup(backup));

liana-gui/src/app/view/message.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{app::menu::Menu, export::ImportExportMessage, node::bitcoind::RpcAuthType};
1+
use crate::{app::menu::Menu, backup, export::ImportExportMessage, node::bitcoind::RpcAuthType};
22
use liana::miniscript::bitcoin::{bip32::Fingerprint, OutPoint};
33

44
pub trait Close {
@@ -89,7 +89,7 @@ pub enum SettingsMessage {
8989
ExportTransactions,
9090
ExportLabels,
9191
ExportWallet,
92-
ExportBackup(String),
92+
ExportBackup(Result<String, backup::Error>),
9393
ImportWallet,
9494
AboutSection,
9595
RegisterWallet,

liana-gui/src/backup.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -45,19 +45,19 @@ pub struct Backup {
4545
pub proprietary: serde_json::Map<String, serde_json::Value>,
4646
}
4747

48-
#[derive(Debug)]
48+
#[derive(Debug, Clone)]
4949
pub enum Error {
5050
DescriptorMissing,
5151
NotSingleWallet,
5252
Json,
5353
SettingsFromFile,
54-
Daemon(DaemonError),
54+
Daemon(String),
5555
TxTimeMissing,
5656
}
5757

5858
impl From<DaemonError> for Error {
5959
fn from(value: DaemonError) -> Self {
60-
Error::Daemon(value)
60+
Error::Daemon(value.to_string())
6161
}
6262
}
6363

liana-gui/src/installer/message.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use std::path::PathBuf;
77
use super::{context, Error};
88
use crate::{
99
app::view::Close,
10+
backup,
1011
download::{DownloadError, Progress},
1112
export::ImportExportMessage,
1213
hw::HardwareWalletMessage,
@@ -46,6 +47,8 @@ pub enum Message {
4647
WalletRegistered(Result<(Fingerprint, Option<[u8; 32]>), Error>),
4748
MnemonicWord(usize, String),
4849
ImportMnemonic(bool),
50+
BackupWallet,
51+
ExportWallet(Result<String, backup::Error>),
4952
ImportExport(ImportExportMessage),
5053
}
5154

@@ -57,7 +60,7 @@ impl Close for Message {
5760

5861
impl From<ImportExportMessage> for Message {
5962
fn from(value: ImportExportMessage) -> Self {
60-
Self::ImportExport(value)
63+
Message::ImportExport(value)
6164
}
6265
}
6366

liana-gui/src/installer/mod.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,11 @@ use std::sync::{Arc, Mutex};
2020

2121
use crate::{
2222
app::{
23-
config as gui_config, settings as gui_settings,
24-
settings::{AuthConfig, Settings, SettingsError, WalletSetting},
23+
config as gui_config,
24+
settings::{self as gui_settings, AuthConfig, Settings, SettingsError, WalletSetting},
2525
wallet::wallet_name,
2626
},
27+
backup,
2728
daemon::DaemonError,
2829
datadir::create_directory,
2930
hw::{HardwareWalletConfig, HardwareWallets},
@@ -708,6 +709,7 @@ pub enum Error {
708709
CannotGetAvailablePort(String),
709710
Unexpected(String),
710711
HardwareWallet(async_hwi::Error),
712+
Backup(backup::Error),
711713
}
712714

713715
impl From<jsonrpc::simple_http::Error> for Error {
@@ -760,6 +762,7 @@ impl std::fmt::Display for Error {
760762
Self::CannotCreateFile(e) => write!(f, "Failed to create file: {}", e),
761763
Self::Unexpected(e) => write!(f, "Unexpected: {}", e),
762764
Self::HardwareWallet(e) => write!(f, "Hardware Wallet: {}", e),
765+
Self::Backup(e) => write!(f, "Backup: {:?}", e),
763766
}
764767
}
765768
}

liana-gui/src/installer/step/descriptor/mod.rs

+67-6
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ use liana_ui::{component::form, widget::Element};
1616
use async_hwi::DeviceKind;
1717

1818
use crate::{
19-
app::wallet::wallet_name,
19+
app::{state::export::ExportModal, wallet::wallet_name},
20+
backup::{self, Backup},
21+
export::{ImportExportMessage, ImportExportType},
2022
hw::{HardwareWallet, HardwareWallets},
2123
installer::{
2224
message::{self, Message},
@@ -302,17 +304,70 @@ pub struct BackupDescriptor {
302304
done: bool,
303305
descriptor: Option<LianaDescriptor>,
304306
key_aliases: HashMap<Fingerprint, String>,
307+
modal: Option<ExportModal>,
308+
error: Option<Error>,
305309
}
306310

307311
impl Step for BackupDescriptor {
312+
fn subscription(&self, _hws: &HardwareWallets) -> Subscription<Message> {
313+
if let Some(modal) = &self.modal {
314+
if let Some(sub) = modal.subscription() {
315+
sub.map(|m| Message::ImportExport(ImportExportMessage::Progress(m)))
316+
} else {
317+
Subscription::none()
318+
}
319+
} else {
320+
Subscription::none()
321+
}
322+
}
308323
fn update(
309324
&mut self,
310325
_hws: &mut HardwareWallets,
311326
message: Message,
312-
_ctx: &Context,
327+
ctx: &Context,
313328
) -> Task<Message> {
314-
if let Message::UserActionDone(done) = message {
315-
self.done = done;
329+
match message {
330+
Message::ImportExport(ImportExportMessage::Close) => {
331+
self.modal = None;
332+
}
333+
Message::ImportExport(m) => {
334+
if let Some(modal) = self.modal.as_mut() {
335+
let task: Task<Message> = modal.update(m);
336+
return task;
337+
};
338+
}
339+
Message::BackupWallet => {
340+
if self.modal.is_none() {
341+
let ctx = ctx.clone();
342+
return Task::perform(
343+
async move {
344+
let backup = Backup::from_installer(ctx, true).await?;
345+
serde_json::to_string_pretty(&backup).map_err(|_| backup::Error::Json)
346+
},
347+
Message::ExportWallet,
348+
);
349+
}
350+
}
351+
Message::ExportWallet(str) => {
352+
if self.modal.is_none() {
353+
let str = match str {
354+
Ok(s) => s,
355+
Err(e) => {
356+
tracing::error!("{e:?}");
357+
self.error = Some(Error::Backup(e));
358+
return Task::none();
359+
}
360+
};
361+
let modal = ExportModal::new(None, ImportExportType::ExportBackup(str));
362+
let launch = modal.launch();
363+
self.modal = Some(modal);
364+
return launch;
365+
}
366+
}
367+
Message::UserActionDone(done) => {
368+
self.done = done;
369+
}
370+
_ => {}
316371
}
317372
Task::none()
318373
}
@@ -334,13 +389,19 @@ impl Step for BackupDescriptor {
334389
progress: (usize, usize),
335390
email: Option<&'a str>,
336391
) -> Element<Message> {
337-
view::backup_descriptor(
392+
let content = view::backup_descriptor(
338393
progress,
339394
email,
340395
self.descriptor.as_ref().expect("Must be a descriptor"),
341396
&self.key_aliases,
397+
self.error.as_ref(),
342398
self.done,
343-
)
399+
);
400+
if let Some(modal) = &self.modal {
401+
modal.view(content)
402+
} else {
403+
content
404+
}
344405
}
345406
}
346407

liana-gui/src/installer/view/mod.rs

+13-4
Original file line numberDiff line numberDiff line change
@@ -687,6 +687,7 @@ pub fn backup_descriptor<'a>(
687687
email: Option<&'a str>,
688688
descriptor: &'a LianaDescriptor,
689689
keys_aliases: &'a HashMap<Fingerprint, String>,
690+
error: Option<&Error>,
690691
done: bool,
691692
) -> Element<'a, Message> {
692693
layout(
@@ -722,6 +723,7 @@ pub fn backup_descriptor<'a>(
722723
))
723724
.max_width(1000),
724725
)
726+
.push_maybe(error.map(|e| card::error("Failed to export backup", e.to_string())))
725727
.push(
726728
card::simple(
727729
Column::new()
@@ -739,10 +741,17 @@ pub fn backup_descriptor<'a>(
739741
),
740742
)
741743
.push(
742-
Row::new().push(Column::new().width(Length::Fill)).push(
743-
button::secondary(Some(icon::clipboard_icon()), "Copy")
744-
.on_press(Message::Clibpboard(descriptor.to_string())),
745-
),
744+
Row::new()
745+
.push(Space::with_width(Length::Fill))
746+
.push(
747+
button::secondary(Some(icon::wallet_icon()), "Backup")
748+
.on_press(Message::BackupWallet),
749+
)
750+
.push(Space::with_width(10))
751+
.push(
752+
button::secondary(Some(icon::clipboard_icon()), "Copy")
753+
.on_press(Message::Clibpboard(descriptor.to_string())),
754+
),
746755
)
747756
.spacing(10),
748757
)

0 commit comments

Comments
 (0)