Merge #469: gui: generate multiple addresses

b25fc7cb43000e2aac7f70692dca5bab2ea8e901 gui: generate multiple addresses (edouard)

Pull request description:

  ![2023-04-27T17:26:10,353548866+02:00](https://user-images.githubusercontent.com/6933020/234910569-02307356-1513-409a-a441-e84ebf92cf27.png)

ACKs for top commit:
  edouardparis:
    Self-ACK b25fc7cb43000e2aac7f70692dca5bab2ea8e901

Tree-SHA512: 613643fafc4c78e053c8fdf416985b658bd42425703971f47c77a8effb4e9c85e2eb401165bf47bf66d313100c196e2c67eeb5051497241f7c1b566d32b4cc8f
This commit is contained in:
edouard 2023-05-04 11:00:17 +02:00
commit 474e23c730
No known key found for this signature in database
GPG Key ID: E65F7A089C20DC8F
3 changed files with 84 additions and 37 deletions

View File

@ -266,23 +266,19 @@ impl From<Home> for Box<dyn State> {
#[derive(Default)] #[derive(Default)]
pub struct ReceivePanel { pub struct ReceivePanel {
address: Option<Address>, addresses: Vec<Address>,
qr_code: Option<qr_code::State>, qr_code: Option<qr_code::State>,
warning: Option<Error>, warning: Option<Error>,
} }
impl State for ReceivePanel { impl State for ReceivePanel {
fn view<'a>(&'a self, cache: &'a Cache) -> Element<'a, view::Message> { fn view<'a>(&'a self, cache: &'a Cache) -> Element<'a, view::Message> {
if let Some(address) = &self.address { view::dashboard(
view::dashboard( &Menu::Receive,
&Menu::Receive, cache,
cache, self.warning.as_ref(),
self.warning.as_ref(), view::receive::receive(&self.addresses, self.qr_code.as_ref()),
view::receive::receive(address, self.qr_code.as_ref().unwrap()), )
)
} else {
view::dashboard(&Menu::Receive, cache, self.warning.as_ref(), Column::new())
}
} }
fn update( fn update(
&mut self, &mut self,
@ -296,7 +292,7 @@ impl State for ReceivePanel {
Ok(address) => { Ok(address) => {
self.warning = None; self.warning = None;
self.qr_code = Some(qr_code::State::new(address.to_qr_uri()).unwrap()); self.qr_code = Some(qr_code::State::new(address.to_qr_uri()).unwrap());
self.address = Some(address); self.addresses.push(address);
} }
Err(e) => self.warning = Some(e), Err(e) => self.warning = Some(e),
} }
@ -367,6 +363,6 @@ mod tests {
let sandbox = sandbox.load(client, &Cache::default()).await; let sandbox = sandbox.load(client, &Cache::default()).await;
let panel = sandbox.state(); let panel = sandbox.state();
assert_eq!(panel.address, Some(addr)); assert_eq!(panel.addresses, vec![addr]);
} }
} }

View File

@ -1,11 +1,15 @@
use iced::{ use iced::{
widget::qr_code::{self, QRCode}, widget::{
qr_code::{self, QRCode},
scrollable, Space,
},
Alignment, Length, Alignment, Length,
}; };
use liana::miniscript::bitcoin; use liana::miniscript::bitcoin;
use liana_ui::{ use liana_ui::{
color,
component::{button, card, text::*}, component::{button, card, text::*},
icon, theme, icon, theme,
widget::*, widget::*,
@ -13,32 +17,68 @@ use liana_ui::{
use super::message::Message; use super::message::Message;
pub fn receive<'a>(address: &'a bitcoin::Address, qr: &'a qr_code::State) -> Element<'a, Message> { pub fn receive<'a>(
addresses: &'a [bitcoin::Address],
qr: Option<&'a qr_code::State>,
) -> Element<'a, Message> {
Column::new() Column::new()
.push(card::simple(
Column::new()
.push(QRCode::new(qr).cell_size(10))
.push(
Row::new()
.push(text(address.to_string()).small())
.push(
Button::new(icon::clipboard_icon())
.on_press(Message::Clipboard(address.to_string()))
.style(theme::Button::TransparentBorder),
)
.align_items(Alignment::Center),
)
.align_items(Alignment::Center)
.spacing(20),
))
.push( .push(
Column::new().push( Row::new()
button::primary(None, "Generate new") .align_items(Alignment::Center)
.on_press(Message::Next) .push(Container::new(h3("Receive")).width(Length::Fill))
.width(Length::Units(150)), .push(
), button::primary(Some(icon::plus_icon()), "Generate address")
.on_press(Message::Next),
),
)
.push(p1_bold("New and never used reception addresses"))
.push(
Row::new()
.spacing(10)
.push(addresses.iter().rev().fold(
Column::new().spacing(10).width(Length::Fill),
|col, address| {
col.push(
card::simple(
Row::new()
.push(
Container::new(
scrollable(
Column::new()
.push(Space::with_height(Length::Units(10)))
.push(
p2_regular(address.to_string())
.small()
.style(color::GREY_3),
)
// Space between the address and the scrollbar
.push(Space::with_height(Length::Units(10))),
)
.horizontal_scroll(
scrollable::Properties::new().scroller_width(5),
),
)
.width(Length::Fill),
)
.push(
Button::new(icon::clipboard_icon().style(color::GREY_3))
.on_press(Message::Clipboard(address.to_string()))
.style(theme::Button::TransparentBorder),
)
.align_items(Alignment::Center),
)
.padding(20),
)
},
))
.push(if let Some(qr) = qr {
Container::new(QRCode::new(qr).cell_size(5))
.padding(10)
.style(theme::Container::QrCode)
} else {
Container::new(Space::with_width(Length::Fill)).width(Length::Units(200))
}),
) )
.spacing(20) .spacing(20)
.align_items(Alignment::Center)
.into() .into()
} }

View File

@ -93,6 +93,7 @@ pub enum Container {
Badge(Badge), Badge(Badge),
Pill(Pill), Pill(Pill),
Custom(iced::Color), Custom(iced::Color),
QrCode,
} }
impl container::StyleSheet for Theme { impl container::StyleSheet for Theme {
@ -125,6 +126,11 @@ impl container::StyleSheet for Theme {
background: (*c).into(), background: (*c).into(),
..container::Appearance::default() ..container::Appearance::default()
}, },
Container::QrCode => container::Appearance {
background: color::WHITE.into(),
border_radius: 25.0,
..container::Appearance::default()
},
}, },
Theme::Dark => match style { Theme::Dark => match style {
Container::Transparent => container::Appearance { Container::Transparent => container::Appearance {
@ -152,6 +158,11 @@ impl container::StyleSheet for Theme {
background: (*c).into(), background: (*c).into(),
..container::Appearance::default() ..container::Appearance::default()
}, },
Container::QrCode => container::Appearance {
background: color::WHITE.into(),
border_radius: 25.0,
..container::Appearance::default()
},
}, },
} }
} }