gui(settings): view & edit Electrum settings

This commit is contained in:
Michael Mallan 2024-09-03 09:02:35 +01:00
parent b570039ff8
commit 2381227216
No known key found for this signature in database
GPG Key ID: 5177CDCEDB0EABEB
3 changed files with 326 additions and 10 deletions

View File

@ -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,

View File

@ -67,6 +67,7 @@ pub enum SpendTxMessage {
pub enum SettingsMessage {
EditBitcoindSettings,
BitcoindSettings(SettingsEditMessage),
ElectrumSettings(SettingsEditMessage),
RescanSettings(SettingsEditMessage),
EditRemoteBackendSettings,
RemoteBackendSettings(RemoteBackendSettingsMessage),

View File

@ -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 {