Merge #715: installer: Finalize installation and start automatically on final step

f771e674faccf150a5210b3abe829d443619c3e6 gui(installer): restrict clicking previous on final step (jp1ac4)
45f91216d5d0991d7aebde8d4c8bc99edaf5521a gui(installer): remove unused fields from final step (jp1ac4)
03c2bd76b8c6c1b721014afb37da7732ddfeb0cd gui(installer): install and start automatically on final step (jp1ac4)

Pull request description:

  This is to resolve #679.

  The following changes have been made:
  - Change title of final step to "Finalize installation"
  - Install and load wallet automatically when reaching final step
  - Remove fields from `Final` step that are no longer used

  This is how the page looks when installing:
  ![image](https://github.com/wizardsardine/liana/assets/121959000/a5f2b35d-4c4b-4316-9b7b-744a19daf597)

  The following is only shown for ~1 sec once wallet has been installed and before loading wallet:
  ![image](https://github.com/wizardsardine/liana/assets/121959000/830240a1-860c-4c89-890a-e1fb91adfc25)

ACKs for top commit:
  edouardparis:
    ACK f771e674faccf150a5210b3abe829d443619c3e6

Tree-SHA512: b5360c77a13e4028331e4072246f1c897aa6fe61b0c8697b33d5c91ed460e55fe76b8808f4779db2b2f0322e27433e5dbb72fd0210cbf77f28a270bd82ea2558
This commit is contained in:
edouard 2023-10-05 11:09:32 +02:00
commit 52eabd6e07
No known key found for this signature in database
GPG Key ID: E65F7A089C20DC8F
3 changed files with 59 additions and 192 deletions

View File

@ -120,7 +120,6 @@ impl Installer {
} }
pub fn update(&mut self, message: Message) -> Command<Message> { pub fn update(&mut self, message: Message) -> Command<Message> {
let hot_signer_fingerprint = self.signer.lock().unwrap().fingerprint();
match message { match message {
Message::CreateWallet => { Message::CreateWallet => {
self.steps = vec![ self.steps = vec![
@ -132,7 +131,7 @@ impl Installer {
SelectBitcoindTypeStep::new().into(), SelectBitcoindTypeStep::new().into(),
InternalBitcoindStep::new(&self.context.data_dir).into(), InternalBitcoindStep::new(&self.context.data_dir).into(),
DefineBitcoind::new().into(), DefineBitcoind::new().into(),
Final::new(hot_signer_fingerprint).into(), Final::new().into(),
]; ];
self.next() self.next()
} }
@ -147,7 +146,7 @@ impl Installer {
SelectBitcoindTypeStep::new().into(), SelectBitcoindTypeStep::new().into(),
InternalBitcoindStep::new(&self.context.data_dir).into(), InternalBitcoindStep::new(&self.context.data_dir).into(),
DefineBitcoind::new().into(), DefineBitcoind::new().into(),
Final::new(hot_signer_fingerprint).into(), Final::new().into(),
]; ];
self.next() self.next()
} }
@ -160,7 +159,7 @@ impl Installer {
SelectBitcoindTypeStep::new().into(), SelectBitcoindTypeStep::new().into(),
InternalBitcoindStep::new(&self.context.data_dir).into(), InternalBitcoindStep::new(&self.context.data_dir).into(),
DefineBitcoind::new().into(), DefineBitcoind::new().into(),
Final::new(hot_signer_fingerprint).into(), Final::new().into(),
]; ];
self.next() self.next()
} }

View File

@ -16,11 +16,13 @@ pub use mnemonic::{BackupMnemonic, RecoverMnemonic};
use std::path::PathBuf; use std::path::PathBuf;
use iced::{Command, Subscription}; use iced::{Command, Subscription};
use liana::miniscript::bitcoin::bip32::Fingerprint;
use liana_ui::widget::*; use liana_ui::widget::*;
use crate::installer::{context::Context, message::Message, view}; use crate::{
bitcoind::Bitcoind,
installer::{context::Context, message::Message, view},
};
pub trait Step { pub trait Step {
fn update(&mut self, _message: Message) -> Command<Message> { fn update(&mut self, _message: Message) -> Command<Message> {
@ -60,42 +62,37 @@ impl From<Welcome> for Box<dyn Step> {
pub struct Final { pub struct Final {
generating: bool, generating: bool,
context: Option<Context>, internal_bitcoind: Option<Bitcoind>,
warning: Option<String>, warning: Option<String>,
config_path: Option<PathBuf>, config_path: Option<PathBuf>,
hot_signer_fingerprint: Fingerprint,
hot_signer_is_not_used: bool,
} }
impl Final { impl Final {
pub fn new(hot_signer_fingerprint: Fingerprint) -> Self { pub fn new() -> Self {
Self { Self {
context: None, internal_bitcoind: None,
generating: false, generating: false,
warning: None, warning: None,
config_path: None, config_path: None,
hot_signer_fingerprint,
hot_signer_is_not_used: false,
} }
} }
} }
impl Default for Final {
fn default() -> Self {
Self::new()
}
}
impl Step for Final { impl Step for Final {
fn load_context(&mut self, ctx: &Context) { fn load_context(&mut self, ctx: &Context) {
self.context = Some(ctx.clone()); self.internal_bitcoind = ctx.internal_bitcoind.clone();
if let Some(signer) = &ctx.recovered_signer { }
self.hot_signer_fingerprint = signer.fingerprint(); fn load(&self) -> Command<Message> {
self.hot_signer_is_not_used = false; if !self.generating && self.config_path.is_none() {
} else if ctx Command::perform(async {}, |_| Message::Install)
.descriptor
.as_ref()
.unwrap()
.to_string()
.contains(&self.hot_signer_fingerprint.to_string())
{
self.hot_signer_is_not_used = false;
} else { } else {
self.hot_signer_is_not_used = true; Command::none()
} }
} }
fn update(&mut self, message: Message) -> Command<Message> { fn update(&mut self, message: Message) -> Command<Message> {
@ -107,7 +104,13 @@ impl Step for Final {
self.config_path = None; self.config_path = None;
self.warning = Some(e.to_string()); self.warning = Some(e.to_string());
} }
Ok(path) => self.config_path = Some(path), Ok(path) => {
self.config_path = Some(path.clone());
let internal_bitcoind = self.internal_bitcoind.clone();
return Command::perform(async {}, move |_| {
Message::Exit(path.clone(), internal_bitcoind)
});
}
} }
} }
Message::Install => { Message::Install => {
@ -121,20 +124,11 @@ impl Step for Final {
} }
fn view(&self, progress: (usize, usize)) -> Element<Message> { fn view(&self, progress: (usize, usize)) -> Element<Message> {
let ctx = self.context.as_ref().unwrap();
let desc = ctx.descriptor.as_ref().unwrap().to_string();
view::install( view::install(
progress, progress,
ctx,
desc,
self.generating, self.generating,
self.config_path.as_ref(), self.config_path.as_ref(),
self.warning.as_ref(), self.warning.as_ref(),
if self.hot_signer_is_not_used {
None
} else {
Some(self.hot_signer_fingerprint)
},
) )
} }
} }

View File

@ -24,7 +24,6 @@ use crate::{
bitcoind::StartInternalBitcoindError, bitcoind::StartInternalBitcoindError,
hw::HardwareWallet, hw::HardwareWallet,
installer::{ installer::{
context::Context,
message::{self, Message}, message::{self, Message},
prompt, prompt,
step::{DownloadState, InstallState}, step::{DownloadState, InstallState},
@ -303,7 +302,7 @@ pub fn define_descriptor<'a>(
.push(Space::with_height(Length::Fixed(20.0))) .push(Space::with_height(Length::Fixed(20.0)))
.spacing(50), .spacing(50),
false, false,
None, Some(Message::Previous),
) )
} }
@ -423,7 +422,7 @@ pub fn import_descriptor<'a>(
.push_maybe(error.map(|e| card::error("Invalid descriptor", e.to_string()))) .push_maybe(error.map(|e| card::error("Invalid descriptor", e.to_string())))
.spacing(50), .spacing(50),
true, true,
None, Some(Message::Previous),
) )
} }
@ -631,7 +630,7 @@ pub fn participate_xpub<'a>(
}) })
.spacing(50), .spacing(50),
true, true,
None, Some(Message::Previous),
) )
} }
@ -723,7 +722,7 @@ pub fn register_descriptor<'a>(
}) })
.spacing(50), .spacing(50),
true, true,
None, Some(Message::Previous),
) )
} }
@ -791,7 +790,7 @@ pub fn backup_descriptor<'a>(
}) })
.spacing(50), .spacing(50),
true, true,
None, Some(Message::Previous),
) )
} }
@ -878,7 +877,7 @@ pub fn define_bitcoin<'a>(
) )
.spacing(50), .spacing(50),
true, true,
None, Some(Message::Previous),
) )
} }
@ -972,7 +971,7 @@ pub fn select_bitcoind_type<'a>(progress: (usize, usize)) -> Element<'a, Message
), ),
), ),
true, true,
None, Some(Message::Previous),
) )
} }
@ -1092,163 +1091,37 @@ pub fn start_internal_bitcoind<'a>(
pub fn install<'a>( pub fn install<'a>(
progress: (usize, usize), progress: (usize, usize),
context: &Context,
descriptor: String,
generating: bool, generating: bool,
config_path: Option<&std::path::PathBuf>, config_path: Option<&std::path::PathBuf>,
warning: Option<&'a String>, warning: Option<&'a String>,
signer: Option<Fingerprint>,
) -> Element<'a, Message> { ) -> Element<'a, Message> {
let prev_msg = if !generating && warning.is_some() {
Some(Message::Previous)
} else {
None
};
layout( layout(
progress, progress,
"Final step", "Finalize installation",
Column::new() Column::new()
.push(text(
"Check your information before finalizing the install process:",
))
.push(
Container::new(
Column::new()
.spacing(10)
.push(
card::simple(
Column::new()
.spacing(5)
.push(text("Descriptor:").small().bold())
.push(text(descriptor).small()),
)
.width(Length::Fill),
)
.push_maybe(if context.hws.is_empty() && signer.is_none() {
None
} else {
Some(
card::simple(
Column::new()
.spacing(5)
.push(text("Registered signing devices:").small().bold())
.push_maybe(if context.hws.is_empty() {
None
} else {
Some(context.hws.iter().fold(
Column::new(),
|acc, hw| {
acc.push(
Row::new()
.spacing(5)
.push_maybe(
context.keys.iter().find_map(|k| {
if k.master_fingerprint == hw.1
{
Some(
text(k.name.clone())
.small()
.bold(),
)
} else {
None
}
}),
)
.push(
text(format!("#{}", hw.1)).small(),
)
.push(text(hw.0.to_string()).small()),
)
},
))
})
.push_maybe(signer.as_ref().map(|fingerprint| {
Row::new()
.spacing(5)
.push_maybe(context.keys.iter().find_map(|k| {
if k.master_fingerprint == *fingerprint {
Some(text(k.name.clone()).small().bold())
} else {
None
}
}))
.push(text(format!("#{}", fingerprint)).small())
.push(text("This computer").small())
})),
)
.width(Length::Fill),
)
})
.push(
card::simple(
Column::new()
.push(text("Bitcoind:").small().bold())
.push(
Row::new()
.spacing(5)
.align_items(Alignment::Center)
.push(text("Cookie path:").small())
.push(
text(format!(
"{}",
context
.bitcoind_config
.as_ref()
.unwrap()
.cookie_path
.to_string_lossy()
))
.small(),
),
)
.push(
Row::new()
.spacing(5)
.align_items(Alignment::Center)
.push(text("Address:").small())
.push(
text(format!(
"{}",
context.bitcoind_config.as_ref().unwrap().addr
))
.small(),
),
),
)
.width(Length::Fill),
),
)
.max_width(1000),
)
.push(Space::with_height(Length::Fixed(50.0)))
.push_maybe(warning.map(|e| card::invalid(text(e)))) .push_maybe(warning.map(|e| card::invalid(text(e))))
.push(if generating { .push(if generating {
Container::new(button::primary(None, "Installing ...").width(Length::Fixed(200.0))) Container::new(text("Installing..."))
} else if let Some(path) = config_path { } else if config_path.is_some() {
Container::new( Container::new(
Column::new() Row::new()
.push(Container::new(text("Installed !"))) .spacing(10)
.push(Container::new(
button::primary(None, "Start")
.on_press(Message::Exit(
path.clone(),
context.internal_bitcoind.clone(),
))
.width(Length::Fixed(200.0)),
))
.align_items(Alignment::Center) .align_items(Alignment::Center)
.spacing(20), .push(icon::circle_check_icon().style(color::GREEN))
.push(text("Installed").style(color::GREEN)),
) )
.padding(50)
.width(Length::Fill)
.center_x()
} else { } else {
Container::new( Container::new(Space::with_height(Length::Fixed(25.0)))
button::primary(None, "Finalize installation")
.on_press(Message::Install)
.width(Length::Fixed(200.0)),
)
}) })
.spacing(10) .spacing(10)
.width(Length::Fill), .width(Length::Fill),
true, true,
None, prev_msg,
) )
} }
@ -1758,7 +1631,7 @@ pub fn backup_mnemonic<'a>(
}) })
.spacing(50), .spacing(50),
true, true,
None, Some(Message::Previous),
) )
} }
@ -1861,7 +1734,7 @@ pub fn recover_mnemonic<'a>(
}) })
.spacing(50), .spacing(50),
true, true,
None, Some(Message::Previous),
) )
} }
@ -1872,6 +1745,10 @@ fn layout<'a>(
padding_left: bool, padding_left: bool,
previous_message: Option<Message>, previous_message: Option<Message>,
) -> Element<'a, Message> { ) -> Element<'a, Message> {
let mut prev_button = button::transparent(Some(icon::previous_icon()), "Previous");
if let Some(msg) = previous_message {
prev_button = prev_button.on_press(msg);
}
Container::new(scrollable( Container::new(scrollable(
Column::new() Column::new()
.width(Length::Fill) .width(Length::Fill)
@ -1880,12 +1757,9 @@ fn layout<'a>(
Row::new() Row::new()
.align_items(Alignment::Center) .align_items(Alignment::Center)
.push( .push(
Container::new( Container::new(prev_button)
button::transparent(Some(icon::previous_icon()), "Previous") .width(Length::FillPortion(2))
.on_press(previous_message.unwrap_or(Message::Previous)), .center_x(),
)
.width(Length::FillPortion(2))
.center_x(),
) )
.push(Container::new(h3(title)).width(Length::FillPortion(8))) .push(Container::new(h3(title)).width(Length::FillPortion(8)))
.push( .push(