Move all network setup in launcher
This commit is contained in:
parent
be768b1a67
commit
9ae33408cb
@ -1,7 +1,4 @@
|
|||||||
use liana::miniscript::{
|
use liana::miniscript::{bitcoin::bip32::Fingerprint, DescriptorPublicKey};
|
||||||
bitcoin::{bip32::Fingerprint, Network},
|
|
||||||
DescriptorPublicKey,
|
|
||||||
};
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use super::Error;
|
use super::Error;
|
||||||
@ -29,7 +26,6 @@ pub enum Message {
|
|||||||
Select(usize),
|
Select(usize),
|
||||||
UseHotSigner,
|
UseHotSigner,
|
||||||
Installed(Result<PathBuf, Error>),
|
Installed(Result<PathBuf, Error>),
|
||||||
Network(Network),
|
|
||||||
CreateTaprootDescriptor(bool),
|
CreateTaprootDescriptor(bool),
|
||||||
SelectBitcoindType(SelectBitcoindTypeMsg),
|
SelectBitcoindType(SelectBitcoindTypeMsg),
|
||||||
InternalBitcoind(InternalBitcoindMsg),
|
InternalBitcoind(InternalBitcoindMsg),
|
||||||
|
|||||||
@ -29,6 +29,7 @@ use step::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub struct Installer {
|
pub struct Installer {
|
||||||
|
network: bitcoin::Network,
|
||||||
current: usize,
|
current: usize,
|
||||||
steps: Vec<Box<dyn Step>>,
|
steps: Vec<Box<dyn Step>>,
|
||||||
hws: HardwareWallets,
|
hws: HardwareWallets,
|
||||||
@ -61,6 +62,7 @@ impl Installer {
|
|||||||
) -> (Installer, Command<Message>) {
|
) -> (Installer, Command<Message>) {
|
||||||
(
|
(
|
||||||
Installer {
|
Installer {
|
||||||
|
network,
|
||||||
current: 0,
|
current: 0,
|
||||||
hws: HardwareWallets::new(destination_path.clone(), network),
|
hws: HardwareWallets::new(destination_path.clone(), network),
|
||||||
steps: vec![Welcome::default().into()],
|
steps: vec![Welcome::default().into()],
|
||||||
@ -135,7 +137,7 @@ impl Installer {
|
|||||||
Message::CreateWallet => {
|
Message::CreateWallet => {
|
||||||
self.steps = vec![
|
self.steps = vec![
|
||||||
Welcome::default().into(),
|
Welcome::default().into(),
|
||||||
DefineDescriptor::new(self.signer.clone()).into(),
|
DefineDescriptor::new(self.network, self.signer.clone()).into(),
|
||||||
BackupMnemonic::new(self.signer.clone()).into(),
|
BackupMnemonic::new(self.signer.clone()).into(),
|
||||||
BackupDescriptor::default().into(),
|
BackupDescriptor::default().into(),
|
||||||
RegisterDescriptor::new_create_wallet().into(),
|
RegisterDescriptor::new_create_wallet().into(),
|
||||||
@ -149,8 +151,8 @@ impl Installer {
|
|||||||
Message::ParticipateWallet => {
|
Message::ParticipateWallet => {
|
||||||
self.steps = vec![
|
self.steps = vec![
|
||||||
Welcome::default().into(),
|
Welcome::default().into(),
|
||||||
ParticipateXpub::new(self.signer.clone()).into(),
|
ParticipateXpub::new(self.network, self.signer.clone()).into(),
|
||||||
ImportDescriptor::new(false).into(),
|
ImportDescriptor::new(self.network).into(),
|
||||||
BackupMnemonic::new(self.signer.clone()).into(),
|
BackupMnemonic::new(self.signer.clone()).into(),
|
||||||
RegisterDescriptor::new_import_wallet().into(),
|
RegisterDescriptor::new_import_wallet().into(),
|
||||||
SelectBitcoindTypeStep::new().into(),
|
SelectBitcoindTypeStep::new().into(),
|
||||||
@ -163,7 +165,7 @@ impl Installer {
|
|||||||
Message::ImportWallet => {
|
Message::ImportWallet => {
|
||||||
self.steps = vec![
|
self.steps = vec![
|
||||||
Welcome::default().into(),
|
Welcome::default().into(),
|
||||||
ImportDescriptor::new(true).into(),
|
ImportDescriptor::new(self.network).into(),
|
||||||
RecoverMnemonic::default().into(),
|
RecoverMnemonic::default().into(),
|
||||||
RegisterDescriptor::new_import_wallet().into(),
|
RegisterDescriptor::new_import_wallet().into(),
|
||||||
SelectBitcoindTypeStep::new().into(),
|
SelectBitcoindTypeStep::new().into(),
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
use std::collections::{BTreeMap, HashMap, HashSet};
|
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
@ -196,11 +195,9 @@ impl Setup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct DefineDescriptor {
|
pub struct DefineDescriptor {
|
||||||
data_dir: Option<PathBuf>,
|
setup: Setup,
|
||||||
setup: HashMap<Network, Setup>,
|
|
||||||
|
|
||||||
network: Network,
|
network: Network,
|
||||||
network_valid: bool,
|
|
||||||
use_taproot: bool,
|
use_taproot: bool,
|
||||||
|
|
||||||
modal: Option<Box<dyn DescriptorEditModal>>,
|
modal: Option<Box<dyn DescriptorEditModal>>,
|
||||||
@ -210,13 +207,11 @@ pub struct DefineDescriptor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DefineDescriptor {
|
impl DefineDescriptor {
|
||||||
pub fn new(signer: Arc<Mutex<Signer>>) -> Self {
|
pub fn new(network: Network, signer: Arc<Mutex<Signer>>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
network: Network::Bitcoin,
|
network,
|
||||||
use_taproot: false,
|
use_taproot: false,
|
||||||
setup: HashMap::from([(Network::Bitcoin, Setup::new())]),
|
setup: Setup::new(),
|
||||||
data_dir: None,
|
|
||||||
network_valid: true,
|
|
||||||
modal: None,
|
modal: None,
|
||||||
signer,
|
signer,
|
||||||
error: None,
|
error: None,
|
||||||
@ -224,25 +219,10 @@ impl DefineDescriptor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn valid(&self) -> bool {
|
fn valid(&self) -> bool {
|
||||||
self.setup[&self.network].valid()
|
self.setup.valid()
|
||||||
}
|
}
|
||||||
fn setup_mut(&mut self) -> &mut Setup {
|
fn setup_mut(&mut self) -> &mut Setup {
|
||||||
self.setup
|
&mut self.setup
|
||||||
.get_mut(&self.network)
|
|
||||||
.expect("There is always one")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_network(&mut self, network: Network) {
|
|
||||||
self.network = network;
|
|
||||||
if self.setup.get(&self.network).is_none() {
|
|
||||||
self.setup.insert(self.network, Setup::new());
|
|
||||||
}
|
|
||||||
self.signer.lock().unwrap().set_network(network);
|
|
||||||
if let Some(mut network_datadir) = self.data_dir.clone() {
|
|
||||||
network_datadir.push(self.network.to_string());
|
|
||||||
self.network_valid = !network_datadir.exists();
|
|
||||||
}
|
|
||||||
self.check_setup()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_setup(&mut self) {
|
fn check_setup(&mut self) {
|
||||||
@ -257,16 +237,11 @@ impl Step for DefineDescriptor {
|
|||||||
// form value is set as valid each time it is edited.
|
// form value is set as valid each time it is edited.
|
||||||
// Verification of the values is happening when the user click on Next button.
|
// Verification of the values is happening when the user click on Next button.
|
||||||
fn update(&mut self, hws: &mut HardwareWallets, message: Message) -> Command<Message> {
|
fn update(&mut self, hws: &mut HardwareWallets, message: Message) -> Command<Message> {
|
||||||
let network = self.network;
|
|
||||||
self.error = None;
|
self.error = None;
|
||||||
match message {
|
match message {
|
||||||
Message::Close => {
|
Message::Close => {
|
||||||
self.modal = None;
|
self.modal = None;
|
||||||
}
|
}
|
||||||
Message::Network(network) => {
|
|
||||||
hws.set_network(network);
|
|
||||||
self.set_network(network)
|
|
||||||
}
|
|
||||||
Message::CreateTaprootDescriptor(use_taproot) => {
|
Message::CreateTaprootDescriptor(use_taproot) => {
|
||||||
self.use_taproot = use_taproot;
|
self.use_taproot = use_taproot;
|
||||||
self.check_setup();
|
self.check_setup();
|
||||||
@ -313,6 +288,7 @@ impl Step for DefineDescriptor {
|
|||||||
}
|
}
|
||||||
message::DefineKey::Edit => {
|
message::DefineKey::Edit => {
|
||||||
let use_taproot = self.use_taproot;
|
let use_taproot = self.use_taproot;
|
||||||
|
let network = self.network;
|
||||||
let setup = self.setup_mut();
|
let setup = self.setup_mut();
|
||||||
let modal = EditXpubModal::new(
|
let modal = EditXpubModal::new(
|
||||||
use_taproot,
|
use_taproot,
|
||||||
@ -424,7 +400,7 @@ impl Step for DefineDescriptor {
|
|||||||
j,
|
j,
|
||||||
self.network,
|
self.network,
|
||||||
self.signer.clone(),
|
self.signer.clone(),
|
||||||
self.setup[&self.network].keys.clone(),
|
self.setup.keys.clone(),
|
||||||
);
|
);
|
||||||
let cmd = modal.load();
|
let cmd = modal.load();
|
||||||
self.modal = Some(Box::new(modal));
|
self.modal = Some(Box::new(modal));
|
||||||
@ -459,11 +435,6 @@ impl Step for DefineDescriptor {
|
|||||||
Command::none()
|
Command::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_context(&mut self, ctx: &Context) {
|
|
||||||
self.data_dir = Some(ctx.data_dir.clone());
|
|
||||||
self.set_network(ctx.bitcoin_config.network)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn subscription(&self, hws: &HardwareWallets) -> Subscription<Message> {
|
fn subscription(&self, hws: &HardwareWallets) -> Subscription<Message> {
|
||||||
if let Some(modal) = &self.modal {
|
if let Some(modal) = &self.modal {
|
||||||
modal.subscription(hws)
|
modal.subscription(hws)
|
||||||
@ -478,9 +449,10 @@ impl Step for DefineDescriptor {
|
|||||||
let mut hw_is_used = false;
|
let mut hw_is_used = false;
|
||||||
let mut spending_keys: Vec<DescriptorPublicKey> = Vec::new();
|
let mut spending_keys: Vec<DescriptorPublicKey> = Vec::new();
|
||||||
let mut key_derivation_index = HashMap::<Fingerprint, usize>::new();
|
let mut key_derivation_index = HashMap::<Fingerprint, usize>::new();
|
||||||
for spending_key in self.setup[&self.network].spending_keys.iter().clone() {
|
for spending_key in self.setup.spending_keys.iter().clone() {
|
||||||
let fingerprint = spending_key.expect("Must be present at this step");
|
let fingerprint = spending_key.expect("Must be present at this step");
|
||||||
let key = self.setup[&self.network]
|
let key = self
|
||||||
|
.setup
|
||||||
.keys
|
.keys
|
||||||
.iter()
|
.iter()
|
||||||
.find(|key| key.key.master_fingerprint() == fingerprint)
|
.find(|key| key.key.master_fingerprint() == fingerprint)
|
||||||
@ -506,11 +478,12 @@ impl Step for DefineDescriptor {
|
|||||||
|
|
||||||
let mut recovery_paths = BTreeMap::new();
|
let mut recovery_paths = BTreeMap::new();
|
||||||
|
|
||||||
for path in &self.setup[&self.network].recovery_paths {
|
for path in &self.setup.recovery_paths {
|
||||||
let mut recovery_keys: Vec<DescriptorPublicKey> = Vec::new();
|
let mut recovery_keys: Vec<DescriptorPublicKey> = Vec::new();
|
||||||
for recovery_key in path.keys.iter().clone() {
|
for recovery_key in path.keys.iter().clone() {
|
||||||
let fingerprint = recovery_key.expect("Must be present at this step");
|
let fingerprint = recovery_key.expect("Must be present at this step");
|
||||||
let key = self.setup[&self.network]
|
let key = self
|
||||||
|
.setup
|
||||||
.keys
|
.keys
|
||||||
.iter()
|
.iter()
|
||||||
.find(|key| key.key.master_fingerprint() == fingerprint)
|
.find(|key| key.key.master_fingerprint() == fingerprint)
|
||||||
@ -544,14 +517,14 @@ impl Step for DefineDescriptor {
|
|||||||
recovery_paths.insert(path.sequence, recovery_keys);
|
recovery_paths.insert(path.sequence, recovery_keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.network_valid || spending_keys.is_empty() {
|
if spending_keys.is_empty() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let spending_keys = if spending_keys.len() == 1 {
|
let spending_keys = if spending_keys.len() == 1 {
|
||||||
PathInfo::Single(spending_keys[0].clone())
|
PathInfo::Single(spending_keys[0].clone())
|
||||||
} else {
|
} else {
|
||||||
PathInfo::Multi(self.setup[&self.network].spending_threshold, spending_keys)
|
PathInfo::Multi(self.setup.spending_threshold, spending_keys)
|
||||||
};
|
};
|
||||||
|
|
||||||
let policy = match if self.use_taproot {
|
let policy = match if self.use_taproot {
|
||||||
@ -576,13 +549,11 @@ impl Step for DefineDescriptor {
|
|||||||
hws: &'a HardwareWallets,
|
hws: &'a HardwareWallets,
|
||||||
progress: (usize, usize),
|
progress: (usize, usize),
|
||||||
) -> Element<'a, Message> {
|
) -> Element<'a, Message> {
|
||||||
let aliases = self.setup[&self.network].keys_aliases();
|
let aliases = self.setup.keys_aliases();
|
||||||
let content = view::define_descriptor(
|
let content = view::define_descriptor(
|
||||||
progress,
|
progress,
|
||||||
self.network,
|
|
||||||
self.network_valid,
|
|
||||||
self.use_taproot,
|
self.use_taproot,
|
||||||
self.setup[&self.network]
|
self.setup
|
||||||
.spending_keys
|
.spending_keys
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
@ -590,10 +561,8 @@ impl Step for DefineDescriptor {
|
|||||||
if let Some(key) = key {
|
if let Some(key) = key {
|
||||||
view::defined_descriptor_key(
|
view::defined_descriptor_key(
|
||||||
aliases.get(key).unwrap().to_string(),
|
aliases.get(key).unwrap().to_string(),
|
||||||
self.setup[&self.network].duplicate_name.contains(key),
|
self.setup.duplicate_name.contains(key),
|
||||||
self.setup[&self.network]
|
self.setup.incompatible_with_tapminiscript.contains(key),
|
||||||
.incompatible_with_tapminiscript
|
|
||||||
.contains(key),
|
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
view::undefined_descriptor_key()
|
view::undefined_descriptor_key()
|
||||||
@ -605,16 +574,16 @@ impl Step for DefineDescriptor {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
self.setup[&self.network].spending_threshold,
|
self.setup.spending_threshold,
|
||||||
self.setup[&self.network]
|
self.setup
|
||||||
.recovery_paths
|
.recovery_paths
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, path)| {
|
.map(|(i, path)| {
|
||||||
path.view(
|
path.view(
|
||||||
&aliases,
|
&aliases,
|
||||||
&self.setup[&self.network].duplicate_name,
|
&self.setup.duplicate_name,
|
||||||
&self.setup[&self.network].incompatible_with_tapminiscript,
|
&self.setup.incompatible_with_tapminiscript,
|
||||||
)
|
)
|
||||||
.map(move |msg| {
|
.map(move |msg| {
|
||||||
Message::DefineDescriptor(message::DefineDescriptor::RecoveryPath(i, msg))
|
Message::DefineDescriptor(message::DefineDescriptor::RecoveryPath(i, msg))
|
||||||
@ -1133,13 +1102,6 @@ pub struct HardwareWalletXpubs {
|
|||||||
error: Option<Error>,
|
error: Option<Error>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HardwareWalletXpubs {
|
|
||||||
fn reset(&mut self) {
|
|
||||||
self.error = None;
|
|
||||||
self.xpubs = Vec::new();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SignerXpubs {
|
pub struct SignerXpubs {
|
||||||
signer: Arc<Mutex<Signer>>,
|
signer: Arc<Mutex<Signer>>,
|
||||||
xpubs: Vec<String>,
|
xpubs: Vec<String>,
|
||||||
@ -1155,11 +1117,6 @@ impl SignerXpubs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset(&mut self) {
|
|
||||||
self.xpubs = Vec::new();
|
|
||||||
self.next_account = ChildNumber::from_hardened_idx(0).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn select(&mut self, network: Network) {
|
fn select(&mut self, network: Network) {
|
||||||
self.next_account = self.next_account.increment().unwrap();
|
self.next_account = self.next_account.increment().unwrap();
|
||||||
let signer = self.signer.lock().unwrap();
|
let signer = self.signer.lock().unwrap();
|
||||||
@ -1180,8 +1137,6 @@ impl SignerXpubs {
|
|||||||
|
|
||||||
pub struct ParticipateXpub {
|
pub struct ParticipateXpub {
|
||||||
network: Network,
|
network: Network,
|
||||||
network_valid: bool,
|
|
||||||
data_dir: Option<PathBuf>,
|
|
||||||
|
|
||||||
shared: bool,
|
shared: bool,
|
||||||
|
|
||||||
@ -1190,33 +1145,14 @@ pub struct ParticipateXpub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ParticipateXpub {
|
impl ParticipateXpub {
|
||||||
pub fn new(signer: Arc<Mutex<Signer>>) -> Self {
|
pub fn new(network: Network, signer: Arc<Mutex<Signer>>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
network: Network::Bitcoin,
|
network,
|
||||||
network_valid: true,
|
|
||||||
data_dir: None,
|
|
||||||
hw_xpubs: Vec::new(),
|
hw_xpubs: Vec::new(),
|
||||||
shared: false,
|
shared: false,
|
||||||
xpubs_signer: SignerXpubs::new(signer),
|
xpubs_signer: SignerXpubs::new(signer),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_network(&mut self, network: Network) {
|
|
||||||
if network != self.network {
|
|
||||||
self.hw_xpubs.iter_mut().for_each(|hw| hw.reset());
|
|
||||||
self.xpubs_signer.reset();
|
|
||||||
}
|
|
||||||
self.network = network;
|
|
||||||
self.xpubs_signer
|
|
||||||
.signer
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.set_network(network);
|
|
||||||
if let Some(mut network_datadir) = self.data_dir.clone() {
|
|
||||||
network_datadir.push(self.network.to_string());
|
|
||||||
self.network_valid = !network_datadir.exists();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Step for ParticipateXpub {
|
impl Step for ParticipateXpub {
|
||||||
@ -1224,10 +1160,6 @@ impl Step for ParticipateXpub {
|
|||||||
// Verification of the values is happening when the user click on Next button.
|
// Verification of the values is happening when the user click on Next button.
|
||||||
fn update(&mut self, hws: &mut HardwareWallets, message: Message) -> Command<Message> {
|
fn update(&mut self, hws: &mut HardwareWallets, message: Message) -> Command<Message> {
|
||||||
match message {
|
match message {
|
||||||
Message::Network(network) => {
|
|
||||||
hws.set_network(network);
|
|
||||||
self.set_network(network);
|
|
||||||
}
|
|
||||||
Message::UserActionDone(shared) => self.shared = shared,
|
Message::UserActionDone(shared) => self.shared = shared,
|
||||||
Message::ImportXpub(fg, res) => {
|
Message::ImportXpub(fg, res) => {
|
||||||
if let Some(hw_xpubs) = self.hw_xpubs.iter_mut().find(|x| x.fingerprint == fg) {
|
if let Some(hw_xpubs) = self.hw_xpubs.iter_mut().find(|x| x.fingerprint == fg) {
|
||||||
@ -1292,11 +1224,6 @@ impl Step for ParticipateXpub {
|
|||||||
hws.refresh().map(Message::HardwareWallets)
|
hws.refresh().map(Message::HardwareWallets)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_context(&mut self, ctx: &Context) {
|
|
||||||
self.data_dir = Some(ctx.data_dir.clone());
|
|
||||||
self.set_network(ctx.bitcoin_config.network);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply(&mut self, ctx: &mut Context) -> bool {
|
fn apply(&mut self, ctx: &mut Context) -> bool {
|
||||||
ctx.bitcoin_config.network = self.network;
|
ctx.bitcoin_config.network = self.network;
|
||||||
// Drop connections to hardware wallets.
|
// Drop connections to hardware wallets.
|
||||||
@ -1307,8 +1234,6 @@ impl Step for ParticipateXpub {
|
|||||||
fn view<'a>(&'a self, hws: &'a HardwareWallets, progress: (usize, usize)) -> Element<Message> {
|
fn view<'a>(&'a self, hws: &'a HardwareWallets, progress: (usize, usize)) -> Element<Message> {
|
||||||
view::participate_xpub(
|
view::participate_xpub(
|
||||||
progress,
|
progress,
|
||||||
self.network,
|
|
||||||
self.network_valid,
|
|
||||||
hws.list
|
hws.list
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
@ -1344,21 +1269,15 @@ impl From<ParticipateXpub> for Box<dyn Step> {
|
|||||||
|
|
||||||
pub struct ImportDescriptor {
|
pub struct ImportDescriptor {
|
||||||
network: Network,
|
network: Network,
|
||||||
network_valid: bool,
|
|
||||||
change_network: bool,
|
|
||||||
data_dir: Option<PathBuf>,
|
|
||||||
imported_descriptor: form::Value<String>,
|
imported_descriptor: form::Value<String>,
|
||||||
wrong_network: bool,
|
wrong_network: bool,
|
||||||
error: Option<String>,
|
error: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ImportDescriptor {
|
impl ImportDescriptor {
|
||||||
pub fn new(change_network: bool) -> Self {
|
pub fn new(network: Network) -> Self {
|
||||||
Self {
|
Self {
|
||||||
change_network,
|
network,
|
||||||
network: Network::Bitcoin,
|
|
||||||
network_valid: true,
|
|
||||||
data_dir: None,
|
|
||||||
imported_descriptor: form::Value::default(),
|
imported_descriptor: form::Value::default(),
|
||||||
wrong_network: false,
|
wrong_network: false,
|
||||||
error: None,
|
error: None,
|
||||||
@ -1397,32 +1316,13 @@ impl Step for ImportDescriptor {
|
|||||||
// form value is set as valid each time it is edited.
|
// form value is set as valid each time it is edited.
|
||||||
// Verification of the values is happening when the user click on Next button.
|
// Verification of the values is happening when the user click on Next button.
|
||||||
fn update(&mut self, _hws: &mut HardwareWallets, message: Message) -> Command<Message> {
|
fn update(&mut self, _hws: &mut HardwareWallets, message: Message) -> Command<Message> {
|
||||||
match message {
|
if let Message::DefineDescriptor(message::DefineDescriptor::ImportDescriptor(desc)) =
|
||||||
Message::Network(network) => {
|
message
|
||||||
self.network = network;
|
{
|
||||||
let mut network_datadir = self.data_dir.clone().unwrap();
|
self.imported_descriptor.value = desc;
|
||||||
network_datadir.push(self.network.to_string());
|
self.check_descriptor(self.network);
|
||||||
self.network_valid = !network_datadir.exists();
|
|
||||||
self.check_descriptor(self.network);
|
|
||||||
}
|
|
||||||
Message::DefineDescriptor(message::DefineDescriptor::ImportDescriptor(desc)) => {
|
|
||||||
self.imported_descriptor.value = desc;
|
|
||||||
self.check_descriptor(self.network);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
};
|
|
||||||
Command::none()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_context(&mut self, ctx: &Context) {
|
|
||||||
if ctx.bitcoin_config.network != self.network {
|
|
||||||
self.check_descriptor(ctx.bitcoin_config.network);
|
|
||||||
}
|
}
|
||||||
self.network = ctx.bitcoin_config.network;
|
Command::none()
|
||||||
self.data_dir = Some(ctx.data_dir.clone());
|
|
||||||
let mut network_datadir = ctx.data_dir.clone();
|
|
||||||
network_datadir.push(self.network.to_string());
|
|
||||||
self.network_valid = !network_datadir.exists();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply(&mut self, ctx: &mut Context) -> bool {
|
fn apply(&mut self, ctx: &mut Context) -> bool {
|
||||||
@ -1441,9 +1341,6 @@ impl Step for ImportDescriptor {
|
|||||||
fn view(&self, _hws: &HardwareWallets, progress: (usize, usize)) -> Element<Message> {
|
fn view(&self, _hws: &HardwareWallets, progress: (usize, usize)) -> Element<Message> {
|
||||||
view::import_descriptor(
|
view::import_descriptor(
|
||||||
progress,
|
progress,
|
||||||
self.change_network,
|
|
||||||
self.network,
|
|
||||||
self.network_valid,
|
|
||||||
&self.imported_descriptor,
|
&self.imported_descriptor,
|
||||||
self.wrong_network,
|
self.wrong_network,
|
||||||
self.error.as_ref(),
|
self.error.as_ref(),
|
||||||
@ -1650,6 +1547,7 @@ impl From<BackupDescriptor> for Box<dyn Step> {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use iced_runtime::command::Action;
|
use iced_runtime::command::Action;
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
pub struct Sandbox<S: Step> {
|
pub struct Sandbox<S: Step> {
|
||||||
@ -1686,9 +1584,10 @@ mod tests {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_define_descriptor_use_hotkey() {
|
async fn test_define_descriptor_use_hotkey() {
|
||||||
let mut ctx = Context::new(Network::Signet, PathBuf::from_str("/").unwrap());
|
let mut ctx = Context::new(Network::Signet, PathBuf::from_str("/").unwrap());
|
||||||
let sandbox: Sandbox<DefineDescriptor> = Sandbox::new(DefineDescriptor::new(Arc::new(
|
let sandbox: Sandbox<DefineDescriptor> = Sandbox::new(DefineDescriptor::new(
|
||||||
Mutex::new(Signer::generate(Network::Bitcoin).unwrap()),
|
Network::Bitcoin,
|
||||||
)));
|
Arc::new(Mutex::new(Signer::generate(Network::Bitcoin).unwrap())),
|
||||||
|
));
|
||||||
|
|
||||||
// Edit primary key
|
// Edit primary key
|
||||||
sandbox
|
sandbox
|
||||||
@ -1767,9 +1666,10 @@ mod tests {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_define_descriptor_stores_if_hw_is_used() {
|
async fn test_define_descriptor_stores_if_hw_is_used() {
|
||||||
let mut ctx = Context::new(Network::Testnet, PathBuf::from_str("/").unwrap());
|
let mut ctx = Context::new(Network::Testnet, PathBuf::from_str("/").unwrap());
|
||||||
let sandbox: Sandbox<DefineDescriptor> = Sandbox::new(DefineDescriptor::new(Arc::new(
|
let sandbox: Sandbox<DefineDescriptor> = Sandbox::new(DefineDescriptor::new(
|
||||||
Mutex::new(Signer::generate(Network::Testnet).unwrap()),
|
Network::Testnet,
|
||||||
)));
|
Arc::new(Mutex::new(Signer::generate(Network::Testnet).unwrap())),
|
||||||
|
));
|
||||||
sandbox.load(&ctx).await;
|
sandbox.load(&ctx).await;
|
||||||
|
|
||||||
let specter_key = message::DefinePath::Key(
|
let specter_key = message::DefinePath::Key(
|
||||||
|
|||||||
@ -32,55 +32,6 @@ use crate::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
enum Network {
|
|
||||||
Mainnet,
|
|
||||||
Testnet,
|
|
||||||
Regtest,
|
|
||||||
Signet,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<bitcoin::Network> for Network {
|
|
||||||
fn from(n: bitcoin::Network) -> Self {
|
|
||||||
match n {
|
|
||||||
bitcoin::Network::Bitcoin => Network::Mainnet,
|
|
||||||
bitcoin::Network::Testnet => Network::Testnet,
|
|
||||||
bitcoin::Network::Regtest => Network::Regtest,
|
|
||||||
bitcoin::Network::Signet => Network::Signet,
|
|
||||||
_ => Network::Mainnet,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Network> for bitcoin::Network {
|
|
||||||
fn from(network: Network) -> bitcoin::Network {
|
|
||||||
match network {
|
|
||||||
Network::Mainnet => bitcoin::Network::Bitcoin,
|
|
||||||
Network::Testnet => bitcoin::Network::Testnet,
|
|
||||||
Network::Regtest => bitcoin::Network::Regtest,
|
|
||||||
Network::Signet => bitcoin::Network::Signet,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Display for Network {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::Mainnet => write!(f, "Bitcoin mainnet"),
|
|
||||||
Self::Testnet => write!(f, "Bitcoin testnet"),
|
|
||||||
Self::Regtest => write!(f, "Bitcoin regtest"),
|
|
||||||
Self::Signet => write!(f, "Bitcoin signet"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const NETWORKS: [Network; 4] = [
|
|
||||||
Network::Mainnet,
|
|
||||||
Network::Testnet,
|
|
||||||
Network::Signet,
|
|
||||||
Network::Regtest,
|
|
||||||
];
|
|
||||||
|
|
||||||
pub fn welcome<'a>() -> Element<'a, Message> {
|
pub fn welcome<'a>() -> Element<'a, Message> {
|
||||||
Container::new(
|
Container::new(
|
||||||
Column::new()
|
Column::new()
|
||||||
@ -191,31 +142,7 @@ impl std::fmt::Display for DescriptorKind {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn define_descriptor_advanced_settings<'a>(
|
pub fn define_descriptor_advanced_settings<'a>(use_taproot: bool) -> Element<'a, Message> {
|
||||||
network: bitcoin::Network,
|
|
||||||
network_valid: bool,
|
|
||||||
use_taproot: bool,
|
|
||||||
) -> Element<'a, Message> {
|
|
||||||
let col_network = Column::new()
|
|
||||||
.spacing(10)
|
|
||||||
.push(text("Network").bold())
|
|
||||||
.push(container(
|
|
||||||
pick_list(&NETWORKS[..], Some(Network::from(network)), |net| {
|
|
||||||
Message::Network(net.into())
|
|
||||||
})
|
|
||||||
.style(if network_valid {
|
|
||||||
theme::PickList::Secondary
|
|
||||||
} else {
|
|
||||||
theme::PickList::Invalid
|
|
||||||
})
|
|
||||||
.padding(10),
|
|
||||||
))
|
|
||||||
.push_maybe(if network_valid {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(text("A data directory already exists for this network").style(color::RED))
|
|
||||||
});
|
|
||||||
|
|
||||||
let col_wallet = Column::new()
|
let col_wallet = Column::new()
|
||||||
.spacing(10)
|
.spacing(10)
|
||||||
.push(text("Descriptor type").bold())
|
.push(text("Descriptor type").bold())
|
||||||
@ -238,12 +165,7 @@ pub fn define_descriptor_advanced_settings<'a>(
|
|||||||
.spacing(20)
|
.spacing(20)
|
||||||
.push(Space::with_height(0))
|
.push(Space::with_height(0))
|
||||||
.push(separation().width(500))
|
.push(separation().width(500))
|
||||||
.push(
|
.push(Row::new().push(col_wallet))
|
||||||
Row::new()
|
|
||||||
.push(col_network)
|
|
||||||
.push(Space::with_width(100))
|
|
||||||
.push(col_wallet),
|
|
||||||
)
|
|
||||||
.push_maybe(if use_taproot {
|
.push_maybe(if use_taproot {
|
||||||
Some(
|
Some(
|
||||||
p1_regular("Taproot is only supported by Liana version 5.0 and above")
|
p1_regular("Taproot is only supported by Liana version 5.0 and above")
|
||||||
@ -259,8 +181,6 @@ pub fn define_descriptor_advanced_settings<'a>(
|
|||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn define_descriptor<'a>(
|
pub fn define_descriptor<'a>(
|
||||||
progress: (usize, usize),
|
progress: (usize, usize),
|
||||||
network: bitcoin::Network,
|
|
||||||
network_valid: bool,
|
|
||||||
use_taproot: bool,
|
use_taproot: bool,
|
||||||
spending_keys: Vec<Element<'a, Message>>,
|
spending_keys: Vec<Element<'a, Message>>,
|
||||||
spending_threshold: usize,
|
spending_threshold: usize,
|
||||||
@ -329,34 +249,29 @@ pub fn define_descriptor<'a>(
|
|||||||
progress,
|
progress,
|
||||||
"Create the wallet",
|
"Create the wallet",
|
||||||
Column::new()
|
Column::new()
|
||||||
.push(
|
.push(collapse::Collapse::new(
|
||||||
collapse::Collapse::new(
|
|| {
|
||||||
|| {
|
Button::new(
|
||||||
Button::new(
|
Row::new()
|
||||||
Row::new()
|
.align_items(Alignment::Center)
|
||||||
.align_items(Alignment::Center)
|
.spacing(10)
|
||||||
.spacing(10)
|
.push(text("Advanced settings").small().bold())
|
||||||
.push(text("Advanced settings").small().bold())
|
.push(icon::collapse_icon()),
|
||||||
.push(icon::collapse_icon()),
|
)
|
||||||
)
|
.style(theme::Button::Transparent)
|
||||||
.style(theme::Button::Transparent)
|
},
|
||||||
},
|
|| {
|
||||||
|| {
|
Button::new(
|
||||||
Button::new(
|
Row::new()
|
||||||
Row::new()
|
.align_items(Alignment::Center)
|
||||||
.align_items(Alignment::Center)
|
.spacing(10)
|
||||||
.spacing(10)
|
.push(text("Advanced settings").small().bold())
|
||||||
.push(text("Advanced settings").small().bold())
|
.push(icon::collapsed_icon()),
|
||||||
.push(icon::collapsed_icon()),
|
)
|
||||||
)
|
.style(theme::Button::Transparent)
|
||||||
.style(theme::Button::Transparent)
|
},
|
||||||
},
|
move || define_descriptor_advanced_settings(use_taproot),
|
||||||
move || {
|
))
|
||||||
define_descriptor_advanced_settings(network, network_valid, use_taproot)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.collapsed(!network_valid),
|
|
||||||
)
|
|
||||||
.push(
|
.push(
|
||||||
Column::new()
|
Column::new()
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
@ -384,7 +299,7 @@ pub fn define_descriptor<'a>(
|
|||||||
))
|
))
|
||||||
.width(Length::Fixed(200.0)),
|
.width(Length::Fixed(200.0)),
|
||||||
)
|
)
|
||||||
.push(if !valid || !network_valid {
|
.push(if !valid {
|
||||||
button::primary(None, "Next").width(Length::Fixed(200.0))
|
button::primary(None, "Next").width(Length::Fixed(200.0))
|
||||||
} else {
|
} else {
|
||||||
button::primary(None, "Next")
|
button::primary(None, "Next")
|
||||||
@ -455,33 +370,10 @@ pub fn recovery_path_view(
|
|||||||
|
|
||||||
pub fn import_descriptor<'a>(
|
pub fn import_descriptor<'a>(
|
||||||
progress: (usize, usize),
|
progress: (usize, usize),
|
||||||
change_network: bool,
|
|
||||||
network: bitcoin::Network,
|
|
||||||
network_valid: bool,
|
|
||||||
imported_descriptor: &form::Value<String>,
|
imported_descriptor: &form::Value<String>,
|
||||||
wrong_network: bool,
|
wrong_network: bool,
|
||||||
error: Option<&String>,
|
error: Option<&String>,
|
||||||
) -> Element<'a, Message> {
|
) -> Element<'a, Message> {
|
||||||
let row_network = Row::new()
|
|
||||||
.spacing(10)
|
|
||||||
.align_items(Alignment::Center)
|
|
||||||
.push(text("Network:").bold())
|
|
||||||
.push(Container::new(
|
|
||||||
pick_list(&NETWORKS[..], Some(Network::from(network)), |net| {
|
|
||||||
Message::Network(net.into())
|
|
||||||
})
|
|
||||||
.style(if network_valid {
|
|
||||||
theme::PickList::Simple
|
|
||||||
} else {
|
|
||||||
theme::PickList::Invalid
|
|
||||||
})
|
|
||||||
.padding(10),
|
|
||||||
))
|
|
||||||
.push_maybe(if network_valid {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(text("A data directory already exists for this network").style(color::RED))
|
|
||||||
});
|
|
||||||
let col_descriptor = Column::new()
|
let col_descriptor = Column::new()
|
||||||
.push(text("Descriptor:").bold())
|
.push(text("Descriptor:").bold())
|
||||||
.push(
|
.push(
|
||||||
@ -501,28 +393,13 @@ pub fn import_descriptor<'a>(
|
|||||||
progress,
|
progress,
|
||||||
"Import the wallet",
|
"Import the wallet",
|
||||||
Column::new()
|
Column::new()
|
||||||
.push(
|
.push(Column::new().spacing(20).push(col_descriptor).push(text(
|
||||||
Column::new()
|
"After creating the wallet, \
|
||||||
.spacing(20)
|
|
||||||
.push_maybe(if change_network {
|
|
||||||
Some(row_network)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
})
|
|
||||||
.push(col_descriptor)
|
|
||||||
.push_maybe(if change_network {
|
|
||||||
// only show message when importing a descriptor
|
|
||||||
Some(text(
|
|
||||||
"After creating the wallet, \
|
|
||||||
you will need to perform a rescan of \
|
you will need to perform a rescan of \
|
||||||
the blockchain in order to see your \
|
the blockchain in order to see your \
|
||||||
coins and past transactions. This can \
|
coins and past transactions. This can \
|
||||||
be done in Settings > Bitcoin Core.",
|
be done in Settings > Bitcoin Core.",
|
||||||
))
|
)))
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.push(
|
.push(
|
||||||
if imported_descriptor.value.is_empty() || !imported_descriptor.valid {
|
if imported_descriptor.value.is_empty() || !imported_descriptor.valid {
|
||||||
button::primary(None, "Next").width(Length::Fixed(200.0))
|
button::primary(None, "Next").width(Length::Fixed(200.0))
|
||||||
@ -684,43 +561,14 @@ pub fn hardware_wallet_xpubs<'a>(
|
|||||||
|
|
||||||
pub fn participate_xpub<'a>(
|
pub fn participate_xpub<'a>(
|
||||||
progress: (usize, usize),
|
progress: (usize, usize),
|
||||||
network: bitcoin::Network,
|
|
||||||
network_valid: bool,
|
|
||||||
hws: Vec<Element<'a, Message>>,
|
hws: Vec<Element<'a, Message>>,
|
||||||
signer: Element<'a, Message>,
|
signer: Element<'a, Message>,
|
||||||
shared: bool,
|
shared: bool,
|
||||||
) -> Element<'a, Message> {
|
) -> Element<'a, Message> {
|
||||||
let row_network = Row::new()
|
|
||||||
.spacing(10)
|
|
||||||
.align_items(Alignment::Center)
|
|
||||||
.push(text("Network:").bold())
|
|
||||||
.push(Container::new(
|
|
||||||
pick_list(&NETWORKS[..], Some(Network::from(network)), |net| {
|
|
||||||
Message::Network(net.into())
|
|
||||||
})
|
|
||||||
.style(if network_valid {
|
|
||||||
theme::PickList::Simple
|
|
||||||
} else {
|
|
||||||
theme::PickList::Invalid
|
|
||||||
})
|
|
||||||
.padding(10),
|
|
||||||
))
|
|
||||||
.push_maybe(if network_valid {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(text("A data directory already exists for this network").style(color::RED))
|
|
||||||
});
|
|
||||||
|
|
||||||
layout(
|
layout(
|
||||||
progress,
|
progress,
|
||||||
"Share your public keys",
|
"Share your public keys",
|
||||||
Column::new()
|
Column::new()
|
||||||
.push(
|
|
||||||
Column::new()
|
|
||||||
.spacing(20)
|
|
||||||
.width(Length::Fill)
|
|
||||||
.push(row_network),
|
|
||||||
)
|
|
||||||
.push(
|
.push(
|
||||||
Column::new()
|
Column::new()
|
||||||
.push(
|
.push(
|
||||||
@ -739,7 +587,7 @@ pub fn participate_xpub<'a>(
|
|||||||
checkbox("I have shared my extended public key", shared)
|
checkbox("I have shared my extended public key", shared)
|
||||||
.on_toggle(Message::UserActionDone),
|
.on_toggle(Message::UserActionDone),
|
||||||
)
|
)
|
||||||
.push(if shared && network_valid {
|
.push(if shared {
|
||||||
button::primary(None, "Next")
|
button::primary(None, "Next")
|
||||||
.width(Length::Fixed(200.0))
|
.width(Length::Fixed(200.0))
|
||||||
.on_press(Message::Next)
|
.on_press(Message::Next)
|
||||||
|
|||||||
@ -2,7 +2,7 @@ use std::path::PathBuf;
|
|||||||
|
|
||||||
use iced::{
|
use iced::{
|
||||||
alignment::Horizontal,
|
alignment::Horizontal,
|
||||||
widget::{tooltip, Space},
|
widget::{scrollable, tooltip},
|
||||||
Alignment, Command, Length, Subscription,
|
Alignment, Command, Length, Subscription,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -28,33 +28,37 @@ fn wallet_name(network: &Network) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Launcher {
|
pub struct Launcher {
|
||||||
choices: Vec<Network>,
|
// true if installed
|
||||||
|
choices: Vec<(Network, bool)>,
|
||||||
datadir_path: PathBuf,
|
datadir_path: PathBuf,
|
||||||
error: Option<String>,
|
error: Option<String>,
|
||||||
delete_wallet_modal: Option<DeleteWalletModal>,
|
delete_wallet_modal: Option<DeleteWalletModal>,
|
||||||
|
collapsed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Launcher {
|
impl Launcher {
|
||||||
pub fn new(datadir_path: PathBuf) -> Self {
|
pub fn new(datadir_path: PathBuf) -> Self {
|
||||||
let mut choices = Vec::new();
|
|
||||||
for network in [
|
|
||||||
Network::Bitcoin,
|
|
||||||
Network::Testnet,
|
|
||||||
Network::Signet,
|
|
||||||
Network::Regtest,
|
|
||||||
] {
|
|
||||||
if datadir_path.join(network.to_string()).exists() {
|
|
||||||
choices.push(network)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Self {
|
Self {
|
||||||
|
choices: [
|
||||||
|
Network::Bitcoin,
|
||||||
|
Network::Testnet,
|
||||||
|
Network::Signet,
|
||||||
|
Network::Regtest,
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.map(|net| (*net, datadir_path.join(net.to_string()).exists()))
|
||||||
|
.collect(),
|
||||||
datadir_path,
|
datadir_path,
|
||||||
choices,
|
|
||||||
error: None,
|
error: None,
|
||||||
delete_wallet_modal: None,
|
delete_wallet_modal: None,
|
||||||
|
collapsed: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_fresh_install(&self) -> bool {
|
||||||
|
!self.choices.iter().any(|(_, installed)| *installed)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn stop(&mut self) {}
|
pub fn stop(&mut self) {}
|
||||||
|
|
||||||
pub fn subscription(&self) -> Subscription<Message> {
|
pub fn subscription(&self) -> Subscription<Message> {
|
||||||
@ -63,9 +67,15 @@ impl Launcher {
|
|||||||
|
|
||||||
pub fn update(&mut self, message: Message) -> Command<Message> {
|
pub fn update(&mut self, message: Message) -> Command<Message> {
|
||||||
match message {
|
match message {
|
||||||
Message::View(ViewMessage::StartInstall) => {
|
Message::View(ViewMessage::ShowUninstalledNetworks) => {
|
||||||
|
self.collapsed = true;
|
||||||
|
Command::none()
|
||||||
|
}
|
||||||
|
Message::View(ViewMessage::StartInstall(net)) => {
|
||||||
let datadir_path = self.datadir_path.clone();
|
let datadir_path = self.datadir_path.clone();
|
||||||
Command::perform(async move { datadir_path }, Message::Install)
|
Command::perform(async move { (datadir_path, net) }, |(d, n)| {
|
||||||
|
Message::Install(d, n)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
Message::View(ViewMessage::Check(network)) => Command::perform(
|
Message::View(ViewMessage::Check(network)) => Command::perform(
|
||||||
check_network_datadir(self.datadir_path.clone(), network),
|
check_network_datadir(self.datadir_path.clone(), network),
|
||||||
@ -88,11 +98,9 @@ impl Launcher {
|
|||||||
}
|
}
|
||||||
Message::View(ViewMessage::DeleteWallet(DeleteWalletMessage::Deleted)) => {
|
Message::View(ViewMessage::DeleteWallet(DeleteWalletMessage::Deleted)) => {
|
||||||
if let Some(modal) = &self.delete_wallet_modal {
|
if let Some(modal) = &self.delete_wallet_modal {
|
||||||
let choices = self.choices.clone();
|
if let Some(choice) = self.choices.iter_mut().find(|c| c.0 == modal.network) {
|
||||||
self.choices = choices
|
choice.1 = false;
|
||||||
.into_iter()
|
}
|
||||||
.filter(|c| c != &modal.network)
|
|
||||||
.collect();
|
|
||||||
}
|
}
|
||||||
Command::none()
|
Command::none()
|
||||||
}
|
}
|
||||||
@ -126,7 +134,7 @@ impl Launcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn view(&self) -> Element<Message> {
|
pub fn view(&self) -> Element<Message> {
|
||||||
let content = Into::<Element<ViewMessage>>::into(
|
let content = Into::<Element<ViewMessage>>::into(scrollable(
|
||||||
Column::new()
|
Column::new()
|
||||||
.push(
|
.push(
|
||||||
Container::new(image::liana_brand_grey().width(Length::Fixed(200.0)))
|
Container::new(image::liana_brand_grey().width(Length::Fixed(200.0)))
|
||||||
@ -136,17 +144,107 @@ impl Launcher {
|
|||||||
Container::new(
|
Container::new(
|
||||||
Column::new()
|
Column::new()
|
||||||
.spacing(30)
|
.spacing(30)
|
||||||
.push(text("Welcome back").size(50).bold())
|
.push(if !self.is_fresh_install() {
|
||||||
|
text("Welcome back").size(50).bold()
|
||||||
|
} else {
|
||||||
|
text("Welcome").size(50).bold()
|
||||||
|
})
|
||||||
.push_maybe(self.error.as_ref().map(|e| card::simple(text(e))))
|
.push_maybe(self.error.as_ref().map(|e| card::simple(text(e))))
|
||||||
.push(
|
.push(if self.is_fresh_install() {
|
||||||
self.choices
|
Column::new()
|
||||||
.iter()
|
.spacing(10)
|
||||||
.fold(
|
.push(
|
||||||
Column::new()
|
Button::new(
|
||||||
.push(text("Select network:").small().bold())
|
Row::new()
|
||||||
.spacing(10),
|
.spacing(20)
|
||||||
|col, choice| {
|
.align_items(Alignment::Center)
|
||||||
col.push(
|
.push(
|
||||||
|
badge::Badge::new(icon::bitcoin_icon())
|
||||||
|
.style(theme::Badge::Bitcoin),
|
||||||
|
)
|
||||||
|
.push(text(format!(
|
||||||
|
"Create wallet on {}",
|
||||||
|
wallet_name(&Network::Bitcoin)
|
||||||
|
))),
|
||||||
|
)
|
||||||
|
.on_press(ViewMessage::StartInstall(Network::Bitcoin))
|
||||||
|
.padding(10)
|
||||||
|
.width(Length::Fixed(400.0))
|
||||||
|
.style(theme::Button::Border),
|
||||||
|
)
|
||||||
|
.push(if !self.collapsed {
|
||||||
|
Column::new().push(
|
||||||
|
Button::new(
|
||||||
|
Row::new()
|
||||||
|
.spacing(20)
|
||||||
|
.align_items(Alignment::Center)
|
||||||
|
.push(badge::Badge::new(icon::plus_icon()))
|
||||||
|
.push(text("Create wallet on another network")),
|
||||||
|
)
|
||||||
|
.on_press(ViewMessage::ShowUninstalledNetworks)
|
||||||
|
.padding(10)
|
||||||
|
.width(Length::Fixed(400.0))
|
||||||
|
.style(theme::Button::TransparentBorder),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
self.choices
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(net, installed)| {
|
||||||
|
if *installed || *net == Network::Bitcoin {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(net)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.fold(Column::new().spacing(10), |col, choice| {
|
||||||
|
col.push(
|
||||||
|
Button::new(
|
||||||
|
Row::new()
|
||||||
|
.spacing(20)
|
||||||
|
.align_items(Alignment::Center)
|
||||||
|
.push(
|
||||||
|
badge::Badge::new(
|
||||||
|
icon::bitcoin_icon(),
|
||||||
|
)
|
||||||
|
.style(theme::Badge::Standard),
|
||||||
|
)
|
||||||
|
.push(text(format!(
|
||||||
|
"Create wallet on {}",
|
||||||
|
wallet_name(choice)
|
||||||
|
))),
|
||||||
|
)
|
||||||
|
.on_press(ViewMessage::StartInstall(*choice))
|
||||||
|
.padding(10)
|
||||||
|
.width(Length::Fixed(400.0))
|
||||||
|
.style(theme::Button::Border),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Column::new()
|
||||||
|
.spacing(10)
|
||||||
|
.push(
|
||||||
|
self.choices
|
||||||
|
.iter()
|
||||||
|
.filter_map(
|
||||||
|
|(net, installed)| {
|
||||||
|
if *installed {
|
||||||
|
Some(net)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.fold(
|
||||||
|
Column::new()
|
||||||
|
.push(
|
||||||
|
text("Open an existing wallet:")
|
||||||
|
.small()
|
||||||
|
.bold(),
|
||||||
|
)
|
||||||
|
.spacing(10),
|
||||||
|
|col, choice| {
|
||||||
|
col.push(
|
||||||
Row::new()
|
Row::new()
|
||||||
.spacing(10)
|
.spacing(10)
|
||||||
.push(
|
.push(
|
||||||
@ -169,7 +267,7 @@ impl Launcher {
|
|||||||
)
|
)
|
||||||
.on_press(ViewMessage::Check(*choice))
|
.on_press(ViewMessage::Check(*choice))
|
||||||
.padding(10)
|
.padding(10)
|
||||||
.width(Length::Fill)
|
.width(Length::Fixed(400.0))
|
||||||
.style(theme::Button::Border),
|
.style(theme::Button::Border),
|
||||||
)
|
)
|
||||||
.push(tooltip::Tooltip::new(
|
.push(tooltip::Tooltip::new(
|
||||||
@ -187,35 +285,89 @@ impl Launcher {
|
|||||||
))
|
))
|
||||||
.align_items(Alignment::Center),
|
.align_items(Alignment::Center),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.push(
|
.push(
|
||||||
Button::new(
|
if !self.collapsed
|
||||||
Row::new()
|
&& self.choices.iter().any(|(_, installed)| !installed)
|
||||||
.spacing(20)
|
{
|
||||||
.align_items(Alignment::Center)
|
Column::new().push(
|
||||||
.push(badge::Badge::new(icon::plus_icon()))
|
Button::new(
|
||||||
.push(text("Install Liana on another network")),
|
Row::new()
|
||||||
)
|
.spacing(20)
|
||||||
.on_press(ViewMessage::StartInstall)
|
.align_items(Alignment::Center)
|
||||||
.padding(10)
|
.push(badge::Badge::new(icon::plus_icon()))
|
||||||
.width(Length::Fill)
|
.push(text("Create a new wallet")),
|
||||||
.style(theme::Button::TransparentBorder),
|
)
|
||||||
),
|
.on_press(ViewMessage::ShowUninstalledNetworks)
|
||||||
)
|
.padding(10)
|
||||||
.max_width(500)
|
.width(Length::Fixed(400.0))
|
||||||
.align_items(Alignment::Center),
|
.style(theme::Button::TransparentBorder),
|
||||||
|
)
|
||||||
|
} else if self.collapsed {
|
||||||
|
self.choices
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(net, installed)| {
|
||||||
|
if *installed {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(net)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.fold(
|
||||||
|
Column::new()
|
||||||
|
.push(
|
||||||
|
text("Create a new wallet:")
|
||||||
|
.small()
|
||||||
|
.bold(),
|
||||||
|
)
|
||||||
|
.spacing(10),
|
||||||
|
|col, choice| {
|
||||||
|
col.push(
|
||||||
|
Button::new(
|
||||||
|
Row::new()
|
||||||
|
.spacing(20)
|
||||||
|
.align_items(Alignment::Center)
|
||||||
|
.push(
|
||||||
|
badge::Badge::new(
|
||||||
|
icon::bitcoin_icon(),
|
||||||
|
)
|
||||||
|
.style(match choice {
|
||||||
|
Network::Bitcoin => {
|
||||||
|
theme::Badge::Bitcoin
|
||||||
|
}
|
||||||
|
_ => theme::Badge::Standard,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.push(text(wallet_name(choice))),
|
||||||
|
)
|
||||||
|
.on_press(ViewMessage::StartInstall(*choice))
|
||||||
|
.padding(10)
|
||||||
|
.width(Length::Fixed(400.0))
|
||||||
|
.style(theme::Button::Border),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Column::new()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.align_items(if self.is_fresh_install() {
|
||||||
|
Alignment::Center
|
||||||
|
} else {
|
||||||
|
Alignment::Start
|
||||||
|
})
|
||||||
|
.max_width(500),
|
||||||
)
|
)
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.height(Length::Fill)
|
.center_x(),
|
||||||
.center_x()
|
),
|
||||||
.center_y(),
|
))
|
||||||
)
|
|
||||||
.push(Space::with_height(Length::Fixed(100.0))),
|
|
||||||
)
|
|
||||||
.map(Message::View);
|
.map(Message::View);
|
||||||
if let Some(modal) = &self.delete_wallet_modal {
|
if let Some(modal) = &self.delete_wallet_modal {
|
||||||
Modal::new(content, modal.view())
|
Modal::new(Container::new(content).height(Length::Fill), modal.view())
|
||||||
.on_blur(Some(Message::View(ViewMessage::DeleteWallet(
|
.on_blur(Some(Message::View(ViewMessage::DeleteWallet(
|
||||||
DeleteWalletMessage::CloseModal,
|
DeleteWalletMessage::CloseModal,
|
||||||
))))
|
))))
|
||||||
@ -229,14 +381,15 @@ impl Launcher {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Message {
|
pub enum Message {
|
||||||
View(ViewMessage),
|
View(ViewMessage),
|
||||||
Install(PathBuf),
|
Install(PathBuf, Network),
|
||||||
Checked(Result<Network, String>),
|
Checked(Result<Network, String>),
|
||||||
Run(PathBuf, app::config::Config, Network),
|
Run(PathBuf, app::config::Config, Network),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum ViewMessage {
|
pub enum ViewMessage {
|
||||||
StartInstall,
|
StartInstall(Network),
|
||||||
|
ShowUninstalledNetworks,
|
||||||
Check(Network),
|
Check(Network),
|
||||||
DeleteWallet(DeleteWalletMessage),
|
DeleteWallet(DeleteWalletMessage),
|
||||||
}
|
}
|
||||||
|
|||||||
@ -198,13 +198,12 @@ impl Application for GUI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
(State::Launcher(l), Message::Launch(msg)) => match *msg {
|
(State::Launcher(l), Message::Launch(msg)) => match *msg {
|
||||||
launcher::Message::Install(datadir_path) => {
|
launcher::Message::Install(datadir_path, network) => {
|
||||||
self.logger.set_installer_mode(
|
self.logger.set_installer_mode(
|
||||||
datadir_path.clone(),
|
datadir_path.clone(),
|
||||||
self.log_level.unwrap_or(LevelFilter::INFO),
|
self.log_level.unwrap_or(LevelFilter::INFO),
|
||||||
);
|
);
|
||||||
let (install, command) =
|
let (install, command) = Installer::new(datadir_path, network);
|
||||||
Installer::new(datadir_path, bitcoin::Network::Bitcoin);
|
|
||||||
self.state = State::Installer(Box::new(install));
|
self.state = State::Installer(Box::new(install));
|
||||||
command.map(|msg| Message::Install(Box::new(msg)))
|
command.map(|msg| Message::Install(Box::new(msg)))
|
||||||
}
|
}
|
||||||
@ -364,13 +363,6 @@ impl Config {
|
|||||||
Err(ConfigError::NotFound) => Ok(Config::Install(datadir_path, network)),
|
Err(ConfigError::NotFound) => Ok(Config::Install(datadir_path, network)),
|
||||||
Err(e) => Err(format!("Failed to read configuration file: {}", e).into()),
|
Err(e) => Err(format!("Failed to read configuration file: {}", e).into()),
|
||||||
}
|
}
|
||||||
} else if !datadir_path.exists()
|
|
||||||
|| (!datadir_path.join("bitcoin").exists()
|
|
||||||
&& !datadir_path.join("testnet").exists()
|
|
||||||
&& !datadir_path.join("signet").exists()
|
|
||||||
&& !datadir_path.join("regtest").exists())
|
|
||||||
{
|
|
||||||
Ok(Config::Install(datadir_path, bitcoin::Network::Bitcoin))
|
|
||||||
} else {
|
} else {
|
||||||
Ok(Config::Launcher(datadir_path))
|
Ok(Config::Launcher(datadir_path))
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user