gui(settings): view & edit Electrum settings
This commit is contained in:
parent
b570039ff8
commit
2381227216
@ -9,7 +9,9 @@ use iced::Command;
|
||||
use tracing::info;
|
||||
|
||||
use liana::{
|
||||
config::{BitcoinBackend, BitcoinConfig, BitcoindConfig, BitcoindRpcAuth, Config},
|
||||
config::{
|
||||
BitcoinBackend, BitcoinConfig, BitcoindConfig, BitcoindRpcAuth, Config, ElectrumConfig,
|
||||
},
|
||||
miniscript::bitcoin::Network,
|
||||
};
|
||||
|
||||
@ -27,6 +29,7 @@ pub struct BitcoindSettingsState {
|
||||
config_updated: bool,
|
||||
|
||||
node_settings: Option<BitcoindSettings>,
|
||||
electrum_settings: Option<ElectrumSettings>,
|
||||
rescan_settings: RescanSetting,
|
||||
}
|
||||
|
||||
@ -37,19 +40,19 @@ impl BitcoindSettingsState {
|
||||
daemon_is_external: bool,
|
||||
bitcoind_is_internal: bool,
|
||||
) -> Self {
|
||||
let bitcoind_config = if let Some(BitcoinBackend::Bitcoind(bitcoind_config)) =
|
||||
config.clone().and_then(|c| c.bitcoin_backend)
|
||||
{
|
||||
Some(bitcoind_config)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let (bitcoind_config, electrum_config) =
|
||||
match config.clone().and_then(|c| c.bitcoin_backend) {
|
||||
Some(BitcoinBackend::Bitcoind(bitcoind_config)) => (Some(bitcoind_config), None),
|
||||
Some(BitcoinBackend::Electrum(electrum_config)) => (None, Some(electrum_config)),
|
||||
_ => (None, None),
|
||||
};
|
||||
BitcoindSettingsState {
|
||||
warning: None,
|
||||
config_updated: false,
|
||||
node_settings: bitcoind_config.map(|bitcoind_config| {
|
||||
BitcoindSettings::new(
|
||||
config
|
||||
.clone()
|
||||
.expect("config must exist if bitcoind_config exists")
|
||||
.bitcoin_config,
|
||||
bitcoind_config,
|
||||
@ -57,6 +60,15 @@ impl BitcoindSettingsState {
|
||||
bitcoind_is_internal,
|
||||
)
|
||||
}),
|
||||
electrum_settings: electrum_config.map(|electrum_config| {
|
||||
ElectrumSettings::new(
|
||||
config
|
||||
.expect("config must exist if electrum_config exists")
|
||||
.bitcoin_config,
|
||||
electrum_config,
|
||||
daemon_is_external,
|
||||
)
|
||||
}),
|
||||
rescan_settings: RescanSetting::new(cache.rescan_progress),
|
||||
}
|
||||
}
|
||||
@ -82,6 +94,14 @@ impl State for BitcoindSettingsState {
|
||||
))
|
||||
});
|
||||
}
|
||||
if let Some(settings) = &mut self.electrum_settings {
|
||||
settings.edited(true);
|
||||
return Command::perform(async {}, |_| {
|
||||
Message::View(view::Message::Settings(
|
||||
view::SettingsMessage::EditBitcoindSettings,
|
||||
))
|
||||
});
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
self.config_updated = false;
|
||||
@ -110,6 +130,13 @@ impl State for BitcoindSettingsState {
|
||||
return settings.update(daemon, cache, msg);
|
||||
}
|
||||
}
|
||||
Message::View(view::Message::Settings(view::SettingsMessage::ElectrumSettings(
|
||||
msg,
|
||||
))) => {
|
||||
if let Some(settings) = &mut self.electrum_settings {
|
||||
return settings.update(daemon, cache, msg);
|
||||
}
|
||||
}
|
||||
Message::View(view::Message::Settings(view::SettingsMessage::RescanSettings(msg))) => {
|
||||
return self.rescan_settings.update(daemon, cache, msg);
|
||||
}
|
||||
@ -121,8 +148,17 @@ impl State for BitcoindSettingsState {
|
||||
fn view<'a>(&'a self, cache: &'a Cache) -> Element<'a, view::Message> {
|
||||
let can_edit_bitcoind_settings =
|
||||
self.node_settings.is_some() && !self.rescan_settings.processing;
|
||||
let can_do_rescan = !self.rescan_settings.processing
|
||||
&& self.node_settings.as_ref().map(|settings| settings.edit) == Some(false);
|
||||
let can_edit_electrum_settings =
|
||||
self.electrum_settings.is_some() && !self.rescan_settings.processing;
|
||||
let settings_edit = self
|
||||
.node_settings
|
||||
.as_ref()
|
||||
.map(|settings| settings.edit)
|
||||
.or(self
|
||||
.electrum_settings
|
||||
.as_ref()
|
||||
.map(|settings| settings.edit));
|
||||
let can_do_rescan = !self.rescan_settings.processing && settings_edit == Some(false);
|
||||
view::settings::bitcoind_settings(
|
||||
cache,
|
||||
self.warning.as_ref(),
|
||||
@ -139,6 +175,19 @@ impl State for BitcoindSettingsState {
|
||||
view::Message::Settings(view::SettingsMessage::RescanSettings(msg))
|
||||
}),
|
||||
]
|
||||
} else if let Some(settings) = &self.electrum_settings {
|
||||
vec![
|
||||
settings
|
||||
.view(cache, can_edit_electrum_settings)
|
||||
.map(move |msg| {
|
||||
view::Message::Settings(view::SettingsMessage::ElectrumSettings(msg))
|
||||
}),
|
||||
self.rescan_settings
|
||||
.view(cache, can_do_rescan)
|
||||
.map(move |msg| {
|
||||
view::Message::Settings(view::SettingsMessage::RescanSettings(msg))
|
||||
}),
|
||||
]
|
||||
} else {
|
||||
vec![self
|
||||
.rescan_settings
|
||||
@ -321,6 +370,105 @@ impl BitcoindSettings {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ElectrumSettings {
|
||||
electrum_config: ElectrumConfig,
|
||||
bitcoin_config: BitcoinConfig,
|
||||
edit: bool,
|
||||
processing: bool,
|
||||
addr: form::Value<String>,
|
||||
daemon_is_external: bool,
|
||||
}
|
||||
|
||||
impl ElectrumSettings {
|
||||
fn new(
|
||||
bitcoin_config: BitcoinConfig,
|
||||
electrum_config: ElectrumConfig,
|
||||
daemon_is_external: bool,
|
||||
) -> ElectrumSettings {
|
||||
let addr = electrum_config.addr.to_string();
|
||||
ElectrumSettings {
|
||||
daemon_is_external,
|
||||
electrum_config,
|
||||
bitcoin_config,
|
||||
edit: false,
|
||||
processing: false,
|
||||
addr: form::Value {
|
||||
valid: true,
|
||||
value: addr,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ElectrumSettings {
|
||||
fn edited(&mut self, success: bool) {
|
||||
self.processing = false;
|
||||
if success {
|
||||
self.edit = false;
|
||||
}
|
||||
}
|
||||
|
||||
fn update(
|
||||
&mut self,
|
||||
daemon: Arc<dyn Daemon + Sync + Send>,
|
||||
_cache: &Cache,
|
||||
message: view::SettingsEditMessage,
|
||||
) -> Command<Message> {
|
||||
match message {
|
||||
view::SettingsEditMessage::Select => {
|
||||
if !self.processing {
|
||||
self.edit = true;
|
||||
}
|
||||
}
|
||||
view::SettingsEditMessage::Cancel => {
|
||||
if !self.processing {
|
||||
self.edit = false;
|
||||
}
|
||||
}
|
||||
view::SettingsEditMessage::FieldEdited(field, value) => {
|
||||
if !self.processing && field == "address" {
|
||||
self.addr.value = value
|
||||
}
|
||||
}
|
||||
view::SettingsEditMessage::Confirm => {
|
||||
if self.addr.valid {
|
||||
let mut daemon_config = daemon.config().cloned().unwrap();
|
||||
daemon_config.bitcoin_backend =
|
||||
Some(liana::config::BitcoinBackend::Electrum(ElectrumConfig {
|
||||
addr: self.addr.value.clone(),
|
||||
}));
|
||||
self.processing = true;
|
||||
return Command::perform(async move { daemon_config }, |cfg| {
|
||||
Message::LoadDaemonConfig(Box::new(cfg))
|
||||
});
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
Command::none()
|
||||
}
|
||||
|
||||
fn view<'a>(&self, cache: &'a Cache, can_edit: bool) -> Element<'a, view::SettingsEditMessage> {
|
||||
if self.edit {
|
||||
view::settings::electrum_edit(
|
||||
self.bitcoin_config.network,
|
||||
cache.blockheight,
|
||||
&self.addr,
|
||||
self.processing,
|
||||
)
|
||||
} else {
|
||||
view::settings::electrum(
|
||||
self.bitcoin_config.network,
|
||||
&self.electrum_config,
|
||||
cache.blockheight,
|
||||
Some(cache.blockheight != 0),
|
||||
can_edit && !self.daemon_is_external,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct RescanSetting {
|
||||
processing: bool,
|
||||
|
||||
@ -67,6 +67,7 @@ pub enum SpendTxMessage {
|
||||
pub enum SettingsMessage {
|
||||
EditBitcoindSettings,
|
||||
BitcoindSettings(SettingsEditMessage),
|
||||
ElectrumSettings(SettingsEditMessage),
|
||||
RescanSettings(SettingsEditMessage),
|
||||
EditRemoteBackendSettings,
|
||||
RemoteBackendSettings(RemoteBackendSettingsMessage),
|
||||
|
||||
@ -532,6 +532,173 @@ pub fn bitcoind<'a>(
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn electrum_edit<'a>(
|
||||
network: Network,
|
||||
blockheight: i32,
|
||||
addr: &form::Value<String>,
|
||||
processing: bool,
|
||||
) -> Element<'a, SettingsEditMessage> {
|
||||
let mut col = Column::new().spacing(20);
|
||||
if blockheight != 0 {
|
||||
col = col
|
||||
.push(
|
||||
Row::new()
|
||||
.push(
|
||||
Row::new()
|
||||
.push(badge::Badge::new(icon::network_icon()))
|
||||
.push(
|
||||
Column::new()
|
||||
.push(text("Network:"))
|
||||
.push(text(network.to_string()).bold()),
|
||||
)
|
||||
.spacing(10)
|
||||
.width(Length::FillPortion(1)),
|
||||
)
|
||||
.push(
|
||||
Row::new()
|
||||
.push(badge::Badge::new(icon::block_icon()))
|
||||
.push(
|
||||
Column::new()
|
||||
.push(text("Block Height:"))
|
||||
.push(text(blockheight.to_string()).bold()),
|
||||
)
|
||||
.spacing(10)
|
||||
.width(Length::FillPortion(1)),
|
||||
),
|
||||
)
|
||||
.push(separation().width(Length::Fill));
|
||||
}
|
||||
|
||||
col = col.push(
|
||||
Column::new()
|
||||
.push(text("Address:").bold().small())
|
||||
.push(
|
||||
form::Form::new_trimmed("127:0.0.1:50001", addr, |value| {
|
||||
SettingsEditMessage::FieldEdited("address", value)
|
||||
})
|
||||
.warning("Please enter a valid address")
|
||||
.size(P1_SIZE)
|
||||
.padding(5),
|
||||
)
|
||||
.spacing(5),
|
||||
);
|
||||
|
||||
let mut cancel_button = button::transparent(None, " Cancel ").padding(5);
|
||||
let mut confirm_button = button::primary(None, " Save ").padding(5);
|
||||
if !processing {
|
||||
cancel_button = cancel_button.on_press(SettingsEditMessage::Cancel);
|
||||
confirm_button = confirm_button.on_press(SettingsEditMessage::Confirm);
|
||||
}
|
||||
|
||||
card::simple(Container::new(
|
||||
Column::new()
|
||||
.push(
|
||||
Row::new()
|
||||
.push(badge::Badge::new(icon::bitcoin_icon()))
|
||||
.push(text("Electrum").bold())
|
||||
.padding(10)
|
||||
.spacing(20)
|
||||
.align_items(Alignment::Center)
|
||||
.width(Length::Fill),
|
||||
)
|
||||
.push(separation().width(Length::Fill))
|
||||
.push(col)
|
||||
.push(
|
||||
Container::new(
|
||||
Row::new()
|
||||
.push(cancel_button)
|
||||
.push(confirm_button)
|
||||
.spacing(10)
|
||||
.align_items(Alignment::Center),
|
||||
)
|
||||
.width(Length::Fill)
|
||||
.align_x(alignment::Horizontal::Right),
|
||||
)
|
||||
.spacing(20),
|
||||
))
|
||||
.width(Length::Fill)
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn electrum<'a>(
|
||||
network: Network,
|
||||
config: &liana::config::ElectrumConfig,
|
||||
blockheight: i32,
|
||||
is_running: Option<bool>,
|
||||
can_edit: bool,
|
||||
) -> Element<'a, SettingsEditMessage> {
|
||||
let mut col = Column::new().spacing(20);
|
||||
if blockheight != 0 {
|
||||
col = col
|
||||
.push(
|
||||
Row::new()
|
||||
.push(
|
||||
Row::new()
|
||||
.push(badge::Badge::new(icon::network_icon()))
|
||||
.push(
|
||||
Column::new()
|
||||
.push(text("Network:"))
|
||||
.push(text(network.to_string()).bold()),
|
||||
)
|
||||
.spacing(10)
|
||||
.width(Length::FillPortion(1)),
|
||||
)
|
||||
.push(
|
||||
Row::new()
|
||||
.push(badge::Badge::new(icon::block_icon()))
|
||||
.push(
|
||||
Column::new()
|
||||
.push(text("Block Height:"))
|
||||
.push(text(blockheight.to_string()).bold()),
|
||||
)
|
||||
.spacing(10)
|
||||
.width(Length::FillPortion(1)),
|
||||
),
|
||||
)
|
||||
.push(separation().width(Length::Fill));
|
||||
}
|
||||
|
||||
let rows = vec![("Address:", config.addr.to_string())];
|
||||
|
||||
let mut col_fields = Column::new();
|
||||
for (k, v) in rows {
|
||||
col_fields = col_fields.push(
|
||||
Row::new()
|
||||
.push(Container::new(text(k).bold().small()).width(Length::Fill))
|
||||
.push(text(v).small()),
|
||||
);
|
||||
}
|
||||
|
||||
card::simple(Container::new(
|
||||
Column::new()
|
||||
.push(
|
||||
Row::new()
|
||||
.push(
|
||||
Row::new()
|
||||
.push(badge::Badge::new(icon::bitcoin_icon()))
|
||||
.push(text("Electrum").bold())
|
||||
.push(is_running_label(is_running))
|
||||
.spacing(20)
|
||||
.align_items(Alignment::Center)
|
||||
.width(Length::Fill),
|
||||
)
|
||||
.push(if can_edit {
|
||||
Button::new(icon::pencil_icon())
|
||||
.style(theme::Button::TransparentBorder)
|
||||
.on_press(SettingsEditMessage::Select)
|
||||
} else {
|
||||
Button::new(icon::pencil_icon()).style(theme::Button::TransparentBorder)
|
||||
})
|
||||
.align_items(Alignment::Center),
|
||||
)
|
||||
.push(separation().width(Length::Fill))
|
||||
.push(col.push(col_fields))
|
||||
.spacing(20),
|
||||
))
|
||||
.width(Length::Fill)
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn is_running_label<'a, T: 'a>(is_running: Option<bool>) -> Container<'a, T> {
|
||||
if let Some(running) = is_running {
|
||||
if running {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user