From e9d77a3a7197e6c2c163a2a1d236991beaeed8e6 Mon Sep 17 00:00:00 2001 From: user Date: Fri, 7 Nov 2025 16:03:11 -0500 Subject: [PATCH] Add bip21 receive display support This adds the scaffolding for the receiver to display bip21 QR codes when a payjoin or other bip21 receive format is required. --- liana-gui/src/app/state/receive.rs | 35 ++++++++++++++++++++++++------ liana-gui/src/app/view/message.rs | 2 +- liana-gui/src/app/view/receive.rs | 6 +++-- 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/liana-gui/src/app/state/receive.rs b/liana-gui/src/app/state/receive.rs index 4df5012f7..f256b6cad 100644 --- a/liana-gui/src/app/state/receive.rs +++ b/liana-gui/src/app/state/receive.rs @@ -1,4 +1,5 @@ use std::collections::{HashMap, HashSet}; +use std::str::FromStr; use std::sync::Arc; use iced::{widget::qr_code, Subscription, Task}; @@ -286,9 +287,21 @@ impl State for ReceivePanel { Task::none() } } - Message::View(view::Message::ShowQrCode(i)) => { - if let (Some(address), Some(index)) = (self.address(i), self.derivation_index(i)) { - if let Some(modal) = ShowQrCodeModal::new(address, *index) { + Message::View(view::Message::ShowQrCode(i, bip21)) => { + if let Some(address) = self.address(i) { + if bip21.is_some() { + if let Some(modal) = + ShowQrCodeModal::new(&bip21.clone().unwrap_or(address.to_string())) + { + self.modal = Modal::ShowQrCode(modal); + } else { + tracing::error!( + "Failed to create QR modal for BIP21 '{:?}' (address {})", + bip21, + address + ); + } + } else if let Some(modal) = ShowQrCodeModal::new(&address.to_string()) { self.modal = Modal::ShowQrCode(modal); } } @@ -420,13 +433,21 @@ pub struct ShowQrCodeModal { } impl ShowQrCodeModal { - pub fn new(address: &Address, index: ChildNumber) -> Option { - qr_code::Data::new(format!("bitcoin:{}?index={}", address, index)) - .ok() - .map(|qr_code| Self { + pub fn new(address: &str) -> Option { + if Address::from_str(address).is_ok() { + qr_code::Data::new(format!("bitcoin:{}", address)) + .ok() + .map(|qr_code| Self { + qr_code, + address: address.to_string(), + }) + } else { + // Already in bip21 format + qr_code::Data::new(address).ok().map(|qr_code| Self { qr_code, address: address.to_string(), }) + } } fn view(&self) -> Element { diff --git a/liana-gui/src/app/view/message.rs b/liana-gui/src/app/view/message.rs index dd874bacf..b317b1593 100644 --- a/liana-gui/src/app/view/message.rs +++ b/liana-gui/src/app/view/message.rs @@ -32,7 +32,7 @@ pub enum Message { Previous, SelectHardwareWallet(usize), CreateRbf(CreateRbfMessage), - ShowQrCode(usize), + ShowQrCode(usize, Option), ImportExport(ImportExportMessage), HideRescanWarning, ExportPsbt, diff --git a/liana-gui/src/app/view/receive.rs b/liana-gui/src/app/view/receive.rs index fd015901a..b4506a328 100644 --- a/liana-gui/src/app/view/receive.rs +++ b/liana-gui/src/app/view/receive.rs @@ -37,6 +37,7 @@ use super::message::Message; fn address_card<'a>( row_index: usize, address: &'a bitcoin::Address, + maybe_bip21: Option, labels: &'a HashMap, labels_editing: &'a HashMap>, ) -> Container<'a, Message> { @@ -83,7 +84,7 @@ fn address_card<'a>( .push(Space::with_width(Length::Fill)) .push( button::secondary(None, "Show QR Code") - .on_press(Message::ShowQrCode(row_index)), + .on_press(Message::ShowQrCode(row_index, maybe_bip21)), ), ) .spacing(10), @@ -129,7 +130,7 @@ pub fn receive<'a>( Column::new().spacing(10).width(Length::Fill), |col, (i, address)| { addresses_count += 1; - col.push(address_card(i, address, labels, labels_editing)) + col.push(address_card(i, address, None, labels, labels_editing)) }, )), ) @@ -229,6 +230,7 @@ pub fn receive<'a>( Button::new(address_card( addresses_count + i, address, + None, prev_labels, labels_editing, ))