diff --git a/src/main.rs b/src/main.rs index 4b08fd0..4495f66 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,13 @@ use bitcoin::Address; use core::run_core; use fedimint_core::api::InviteCode; -use fedimint_core::Amount; use fedimint_ln_common::lightning_invoice::Bolt11Invoice; use iced::widget::qr_code::Data; use routes::Route; use std::str::FromStr; use std::sync::Arc; -use bridge::{CoreUIMsg, SendSuccessMsg}; +use bridge::{CoreUIMsg, ReceiveSuccessMsg, SendSuccessMsg}; use iced::subscription::Subscription; use iced::widget::row; use iced::Element; @@ -53,7 +52,7 @@ enum SendStatus { Sending, } -#[derive(Default, Debug, Clone)] +#[derive(Default, Debug, Clone, PartialEq)] enum ReceiveStatus { #[default] Idle, @@ -75,8 +74,8 @@ pub enum Message { UIHandlerLoaded(Arc), // Local state changes Navigate(Route), - TransferAmountChanged(String), ReceiveAmountChanged(String), + ReceiveStateReset, SendDestInputChanged(String), SendAmountInputChanged(String), SendStateReset, @@ -98,11 +97,11 @@ pub enum Message { // This is the UI state. It should only contain data that is directly rendered by the UI // More complicated state should be in Core, and bridged to the UI in a UI-friendly format. +#[derive(Default, Debug)] pub struct HarborWallet { ui_handle: Option>, - balance: Amount, + balance_sats: u64, active_route: Route, - transfer_amount_str: String, send_status: SendStatus, send_failure_reason: Option, send_success_msg: Option, @@ -112,6 +111,7 @@ pub struct HarborWallet { unlock_status: UnlockStatus, unlock_failure_reason: Option, receive_failure_reason: Option, + receive_success_msg: Option, receive_status: ReceiveStatus, receive_amount_str: String, receive_invoice: Option, @@ -122,39 +122,7 @@ pub struct HarborWallet { donate_amount_str: String, } -impl Default for HarborWallet { - fn default() -> Self { - Self::new() - } -} - impl HarborWallet { - fn new() -> Self { - Self { - ui_handle: None, - balance: Amount::ZERO, - active_route: Route::Unlock, - transfer_amount_str: String::new(), - receive_amount_str: String::new(), - send_dest_input_str: String::new(), - send_amount_input_str: String::new(), - send_status: SendStatus::Idle, - send_failure_reason: None, - send_success_msg: None, - unlock_status: UnlockStatus::Locked, - unlock_failure_reason: None, - password_input_str: String::new(), - receive_failure_reason: None, - receive_status: ReceiveStatus::Idle, - receive_invoice: None, - receive_address: None, - receive_qr_data: None, - mint_invite_code_str: String::new(), - add_federation_failure_reason: None, - donate_amount_str: String::new(), - } - } - fn subscription(&self) -> Subscription { run_core() } @@ -233,10 +201,6 @@ impl HarborWallet { self.active_route = route; Command::none() } - Message::TransferAmountChanged(amount) => { - self.transfer_amount_str = amount; - Command::none() - } Message::ReceiveAmountChanged(amount) => { self.receive_amount_str = amount; Command::none() @@ -269,6 +233,16 @@ impl HarborWallet { self.send_status = SendStatus::Idle; Command::none() } + Message::ReceiveStateReset => { + self.receive_failure_reason = None; + self.receive_amount_str = String::new(); + self.receive_invoice = None; + self.receive_success_msg = None; + self.receive_address = None; + self.receive_qr_data = None; + self.receive_status = ReceiveStatus::Idle; + Command::none() + } // Async commands we fire from the UI to core Message::Noop => Command::none(), Message::Send(invoice_str) => match self.send_status { @@ -378,7 +352,7 @@ impl HarborWallet { } CoreUIMsg::ReceiveSuccess(params) => { info!("Receive success: {params:?}"); - self.receive_status = ReceiveStatus::Idle; + self.receive_success_msg = Some(params); Command::none() } CoreUIMsg::ReceiveFailed(reason) => { @@ -387,7 +361,7 @@ impl HarborWallet { Command::none() } CoreUIMsg::BalanceUpdated(balance) => { - self.balance = balance; + self.balance_sats = balance.sats_round_down(); Command::none() } CoreUIMsg::ReceiveGenerating => { diff --git a/src/routes/home.rs b/src/routes/home.rs index 2914d74..1404273 100644 --- a/src/routes/home.rs +++ b/src/routes/home.rs @@ -7,7 +7,7 @@ use crate::{HarborWallet, Message}; use super::Route; pub fn home(harbor: &HarborWallet) -> Element { - let balance = text(format!("{} sats", harbor.balance.sats_round_down())).size(64); + let balance = text(format!("{} sats", harbor.balance_sats)).size(64); let send_button = h_button("Send", SvgIcon::UpRight, false).on_press(Message::Navigate(Route::Send)); let receive_button = diff --git a/src/routes/receive.rs b/src/routes/receive.rs index b440c97..03e129b 100644 --- a/src/routes/receive.rs +++ b/src/routes/receive.rs @@ -1,10 +1,10 @@ use iced::widget::container::Style; use iced::widget::{column, container, qr_code, scrollable, text}; -use iced::{Border, Element, Padding}; +use iced::{Border, Element, Font, Padding}; use iced::{Color, Length}; use crate::components::{h_button, h_header, h_input, SvgIcon}; -use crate::{HarborWallet, Message}; +use crate::{HarborWallet, Message, ReceiveStatus}; pub fn receive(harbor: &HarborWallet) -> Element { let receive_string = harbor @@ -13,7 +13,18 @@ pub fn receive(harbor: &HarborWallet) -> Element { .map(|i| i.to_string()) .or_else(|| harbor.receive_address.as_ref().map(|a| a.to_string())); - let column = if let Some(string) = receive_string { + let success_message = harbor.receive_success_msg.as_ref().map(|r| { + text(format!("Success: {r:?}")) + .size(32) + .color(Color::from_rgb(0., 255., 0.)) + }); + + let column = if let Some(success_message) = success_message { + let header = h_header("Success!", "You did a good job!"); + let reset_button = + h_button("Start over", SvgIcon::Squirrel, false).on_press(Message::ReceiveStateReset); + column![header, success_message, reset_button] + } else if let Some(string) = receive_string { let header = h_header("Receive", "Scan this QR or copy the string."); let data = harbor.receive_qr_data.as_ref().unwrap(); @@ -30,14 +41,20 @@ pub fn receive(harbor: &HarborWallet) -> Element { ..Style::default() }); - let first_ten_chars = string.chars().take(10).collect::(); + let first_20_chars = string.chars().take(20).collect::(); column![ header, qr_container, - text(format!("{first_ten_chars}...")).size(16), + text(format!("{first_20_chars}...")).size(16).font(Font { + family: iced::font::Family::Monospace, + weight: iced::font::Weight::Normal, + stretch: iced::font::Stretch::Normal, + style: iced::font::Style::Normal, + }), h_button("Copy to clipboard", SvgIcon::Copy, false) .on_press(Message::CopyToClipboard(string)), + h_button("Start over", SvgIcon::Squirrel, false).on_press(Message::ReceiveStateReset), ] } else { let header = h_header("Receive", "Receive on-chain or via lightning."); @@ -53,10 +70,13 @@ pub fn receive(harbor: &HarborWallet) -> Element { Some("sats"), ); - let generate_button = h_button("Generate Invoice", SvgIcon::DownLeft, false) + // TODO how to separate lighting and onchain? + let generating = harbor.receive_status == ReceiveStatus::Generating; + + let generate_button = h_button("Generate Invoice", SvgIcon::DownLeft, generating) .on_press(Message::GenerateInvoice); - let generate_address_button = h_button("Generate Address", SvgIcon::Squirrel, false) + let generate_address_button = h_button("Generate Address", SvgIcon::Squirrel, generating) .on_press(Message::GenerateAddress); column![ diff --git a/src/routes/transfer.rs b/src/routes/transfer.rs index bd55dbb..8184df7 100644 --- a/src/routes/transfer.rs +++ b/src/routes/transfer.rs @@ -1,22 +1,17 @@ -use iced::widget::{column, container, scrollable, text_input}; -use iced::Length; -use iced::{Alignment, Element}; +use iced::widget::{column, container, scrollable}; +use iced::Element; +use iced::{Length, Padding}; +use crate::components::h_header; use crate::{HarborWallet, Message}; -pub fn transfer(harbor: &HarborWallet) -> Element { - container( - scrollable( - column![ - "Let's transfer some ecash!", - text_input("how much?", &harbor.transfer_amount_str) - .on_input(Message::TransferAmountChanged,) - ] - .spacing(32) - .align_items(Alignment::Center) - .width(Length::Fill), - ) - .height(Length::Fill), - ) +pub fn transfer(_harbor: &HarborWallet) -> Element { + container(scrollable( + column![h_header("Transfer", "Coming soon!")] + .spacing(48) + .width(Length::Fill) + .max_width(512) + .padding(Padding::new(48.)), + )) .into() }