diff --git a/Cargo.lock b/Cargo.lock index 3f47fba1..291432d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2789,6 +2789,25 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +[[package]] +name = "is-docker" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" +dependencies = [ + "once_cell", +] + +[[package]] +name = "is-wsl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" +dependencies = [ + "is-docker", + "once_cell", +] + [[package]] name = "itertools" version = "0.10.5" @@ -3063,6 +3082,7 @@ dependencies = [ "lianad", "libc", "log", + "open", "reqwest", "rfd", "rust-ini", @@ -3852,6 +3872,17 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "open" +version = "5.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2483562e62ea94312f3576a7aca397306df7990b8d89033e18766744377ef95" +dependencies = [ + "is-wsl", + "libc", + "pathdiff", +] + [[package]] name = "option-ext" version = "0.2.0" @@ -4010,6 +4041,12 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pathdiff" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" + [[package]] name = "percent-encoding" version = "2.3.1" diff --git a/liana-gui/Cargo.toml b/liana-gui/Cargo.toml index 74b46a73..abd16fed 100644 --- a/liana-gui/Cargo.toml +++ b/liana-gui/Cargo.toml @@ -55,6 +55,8 @@ reqwest = { version = "0.11", default-features=false, features = ["json", "rustl rust-ini = "0.19.0" rfd = "0.15.1" +# Used for opening URLs in browser +open = "5.3" [target.'cfg(windows)'.dependencies] zip = { version = "0.6", default-features=false, features = ["bzip2", "deflate"] } diff --git a/liana-gui/src/app/mod.rs b/liana-gui/src/app/mod.rs index 03c06431..c0316d8c 100644 --- a/liana-gui/src/app/mod.rs +++ b/liana-gui/src/app/mod.rs @@ -388,6 +388,12 @@ impl App { ) } Message::View(view::Message::Menu(menu)) => self.set_current_panel(menu), + Message::View(view::Message::OpenUrl(url)) => { + if let Err(e) = open::that_detached(&url) { + tracing::error!("Error opening '{}': {}", url, e); + } + Task::none() + } Message::View(view::Message::Clipboard(text)) => clipboard::write(text), _ => self .panels diff --git a/liana-gui/src/app/state/settings/bitcoind.rs b/liana-gui/src/app/state/settings/bitcoind.rs index 2fa22be2..dcfa127e 100644 --- a/liana-gui/src/app/state/settings/bitcoind.rs +++ b/liana-gui/src/app/state/settings/bitcoind.rs @@ -18,6 +18,7 @@ use liana_ui::{component::form, widget::Element}; use crate::{ app::{cache::Cache, error::Error, message::Message, state::settings::State, view}, daemon::Daemon, + help, node::{ bitcoind::{RpcAuthType, RpcAuthValues}, NodeType, @@ -196,6 +197,10 @@ impl State for BitcoindSettingsState { }, )) } + setting_panels.push(view::settings::link( + help::CHANGE_BACKEND_OR_NODE_URL, + "I want to change node type or use Liana Connect", + )); setting_panels.push(self.rescan_settings.view(cache, can_do_rescan).map( move |msg| view::Message::Settings(view::SettingsMessage::RescanSettings(msg)), )); diff --git a/liana-gui/src/app/view/message.rs b/liana-gui/src/app/view/message.rs index 8aa88273..d956b850 100644 --- a/liana-gui/src/app/view/message.rs +++ b/liana-gui/src/app/view/message.rs @@ -30,6 +30,7 @@ pub enum Message { HideRescanWarning, ExportPsbt, ImportPsbt, + OpenUrl(String), } impl Close for Message { diff --git a/liana-gui/src/app/view/settings.rs b/liana-gui/src/app/view/settings.rs index 90131539..034dc37c 100644 --- a/liana-gui/src/app/view/settings.rs +++ b/liana-gui/src/app/view/settings.rs @@ -32,6 +32,7 @@ use crate::{ settings::ProviderKey, view::{hw, warning::warn}, }, + help, hw::HardwareWallet, node::{ bitcoind::{RpcAuthType, RpcAuthValues}, @@ -165,6 +166,18 @@ pub fn list(cache: &Cache, is_remote_backend: bool) -> Element { ) } +pub fn link<'a>(url: &str, link_text: &'static str) -> Element<'a, Message> { + iced_tooltip::Tooltip::new( + button::link(Some(icon::link_icon()), link_text) + .on_press(Message::OpenUrl(url.to_string())), + Container::new(text(url)) + .style(theme::card::simple) + .padding(10), + iced_tooltip::Position::Bottom, + ) + .into() +} + pub fn bitcoind_settings<'a>( cache: &'a Cache, warning: Option<&Error>, @@ -345,7 +358,14 @@ pub fn remote_backend_section<'a>( &Menu::Settings, cache, warning, - Column::new().spacing(20).push(header).push(content), + Column::new() + .spacing(20) + .push(header) + .push(content) + .push(link( + help::CHANGE_BACKEND_OR_NODE_URL, + "I want to connect to my own node", + )), ) } diff --git a/liana-gui/src/help.rs b/liana-gui/src/help.rs new file mode 100644 index 00000000..f10118b9 --- /dev/null +++ b/liana-gui/src/help.rs @@ -0,0 +1,3 @@ +/// URL of help page with information about how to change backend/node. +pub const CHANGE_BACKEND_OR_NODE_URL: &str = + "https://wizardsardine.com/liana/support/nodechoiceandswitch/"; diff --git a/liana-gui/src/installer/message.rs b/liana-gui/src/installer/message.rs index 0cbee993..606f979c 100644 --- a/liana-gui/src/installer/message.rs +++ b/liana-gui/src/installer/message.rs @@ -32,7 +32,7 @@ use crate::{ pub enum Message { UserActionDone(bool), Exit(Box, Option), - Clibpboard(String), + Clipboard(String), Next, Skip, Previous, @@ -67,6 +67,7 @@ pub enum Message { WalletFromBackup((HashMap, Backup)), WalletAliasEdited(String), SelectAccount(Fingerprint, ChildNumber), + OpenUrl(String), } impl Close for Message { diff --git a/liana-gui/src/installer/mod.rs b/liana-gui/src/installer/mod.rs index 7598bce7..a8436e84 100644 --- a/liana-gui/src/installer/mod.rs +++ b/liana-gui/src/installer/mod.rs @@ -249,7 +249,13 @@ impl Installer { Task::none() } }, - Message::Clibpboard(s) => clipboard::write(s), + Message::Clipboard(s) => clipboard::write(s), + Message::OpenUrl(url) => { + if let Err(e) = open::that_detached(&url) { + tracing::error!("Error opening '{}': {}", url, e); + } + Task::none() + } Message::Next => self.next(), Message::Previous => self.previous(), Message::Install => { diff --git a/liana-gui/src/installer/step/descriptor/editor/mod.rs b/liana-gui/src/installer/step/descriptor/editor/mod.rs index b5fd9716..48b3783d 100644 --- a/liana-gui/src/installer/step/descriptor/editor/mod.rs +++ b/liana-gui/src/installer/step/descriptor/editor/mod.rs @@ -267,7 +267,7 @@ impl Step for DefineDescriptor { } message::DefinePath::Key(j, msg) => match msg { message::DefineKey::Clipboard(key) => { - return Task::perform(async move { key }, Message::Clibpboard); + return Task::perform(async move { key }, Message::Clipboard); } message::DefineKey::Edit => { diff --git a/liana-gui/src/installer/view/mod.rs b/liana-gui/src/installer/view/mod.rs index 37876dcf..2b5ae66e 100644 --- a/liana-gui/src/installer/view/mod.rs +++ b/liana-gui/src/installer/view/mod.rs @@ -34,6 +34,7 @@ use liana_ui::{ use crate::node::electrum::validate_domain_checkbox; use crate::{ app::settings, + help, hw::{is_compatible_with_tapminiscript, HardwareWallet, UnsupportedReason}, installer::{ descriptor::{Key, PathSequence, PathWarning}, @@ -698,7 +699,7 @@ pub fn register_descriptor<'a>( .push( Row::new().push(Column::new().width(Length::Fill)).push( button::secondary(Some(icon::clipboard_icon()), "Copy") - .on_press(Message::Clibpboard(descriptor_str)), + .on_press(Message::Clipboard(descriptor_str)), ), ) .spacing(10), @@ -845,7 +846,7 @@ pub fn backup_descriptor<'a>( .push(Space::with_width(10)) .push( button::secondary(Some(icon::clipboard_icon()), "Copy") - .on_press(Message::Clibpboard(descriptor.to_string())), + .on_press(Message::Clipboard(descriptor.to_string())), ), ) .spacing(10), @@ -2075,6 +2076,20 @@ pub fn choose_backend(progress: (usize, usize)) -> Element<'static, Message> { .width(Length::FillPortion(1)), ), ) + .push(Space::with_height(20)) // ensures mouse cursor is not already on link when arriving at this step + .push(tooltip::Tooltip::new( + button::link( + Some(icon::link_icon()), + "More information about backend and node options", + ) + .on_press(Message::OpenUrl( + help::CHANGE_BACKEND_OR_NODE_URL.to_string(), + )), + Container::new(text(help::CHANGE_BACKEND_OR_NODE_URL)) + .style(theme::card::simple) + .padding(10), + tooltip::Position::Bottom, + )) .spacing(20), true, Some(Message::Previous), diff --git a/liana-gui/src/lib.rs b/liana-gui/src/lib.rs index 8af17fff..8271bef8 100644 --- a/liana-gui/src/lib.rs +++ b/liana-gui/src/lib.rs @@ -6,6 +6,7 @@ pub mod dir; pub mod download; pub mod export; pub mod gui; +pub mod help; pub mod hw; pub mod installer; pub mod launcher; diff --git a/liana-ui/src/component/button.rs b/liana-ui/src/component/button.rs index 6de615f6..b492a724 100644 --- a/liana-ui/src/component/button.rs +++ b/liana-ui/src/component/button.rs @@ -63,6 +63,10 @@ pub fn transparent_border<'a, T: 'a>(icon: Option>, t: &'static str) -> button(content(icon, text(t))).style(theme::button::container_border) } +pub fn link<'a, T: 'a>(icon: Option>, t: &'static str) -> Button<'a, T> { + Button::new(content(icon, text(t))).style(theme::button::link) +} + fn content<'a, T: 'a>(icon: Option>, text: Text<'a>) -> Container<'a, T> { match icon { None => container(text).align_x(Horizontal::Center).padding(5), diff --git a/liana-ui/src/icon.rs b/liana-ui/src/icon.rs index cfb6b4e3..1fd9385e 100644 --- a/liana-ui/src/icon.rs +++ b/liana-ui/src/icon.rs @@ -131,6 +131,10 @@ pub fn restore_icon() -> Text<'static> { bootstrap_icon('\u{F358}') } +pub fn link_icon() -> Text<'static> { + bootstrap_icon('\u{F470}') +} + const ICONEX_ICONS: Font = Font::with_name("Untitled1"); fn iconex_icon(unicode: char) -> Text<'static> { diff --git a/liana-ui/src/theme/button.rs b/liana-ui/src/theme/button.rs index 9e7951a2..69beddd3 100644 --- a/liana-ui/src/theme/button.rs +++ b/liana-ui/src/theme/button.rs @@ -52,6 +52,10 @@ pub fn transparent_border(theme: &Theme, status: Status) -> Style { button(&theme.colors.buttons.transparent_border, status) } +pub fn link(theme: &Theme, status: Status) -> Style { + button(&theme.colors.buttons.link, status) +} + fn button(p: &Button, status: Status) -> Style { match status { Status::Active => Style { diff --git a/liana-ui/src/theme/palette.rs b/liana-ui/src/theme/palette.rs index 6387705a..e2c69f50 100644 --- a/liana-ui/src/theme/palette.rs +++ b/liana-ui/src/theme/palette.rs @@ -46,6 +46,7 @@ pub struct Buttons { pub container_border: Button, pub menu: Button, pub tab: Button, + pub link: Button, } #[derive(Debug, Copy, Clone, PartialEq)] @@ -386,6 +387,28 @@ impl std::default::Default for Palette { border: color::GREY_7.into(), }), }, + link: Button { + active: ButtonPalette { + background: color::TRANSPARENT, + text: color::GREY_2, + border: color::TRANSPARENT.into(), + }, + hovered: ButtonPalette { + background: color::TRANSPARENT, + text: color::GREEN, + border: color::TRANSPARENT.into(), + }, + pressed: Some(ButtonPalette { + background: color::TRANSPARENT, + text: color::GREEN, + border: color::TRANSPARENT.into(), + }), + disabled: Some(ButtonPalette { + background: color::TRANSPARENT, + text: color::GREY_2, + border: color::TRANSPARENT.into(), + }), + }, }, cards: Cards { simple: ContainerPalette {