diff --git a/src/bridge.rs b/src/bridge.rs index 140f3ac..bdf7b00 100644 --- a/src/bridge.rs +++ b/src/bridge.rs @@ -1,5 +1,5 @@ -use fedimint_core::Amount; use fedimint_core::api::InviteCode; +use fedimint_core::Amount; use fedimint_ln_common::lightning_invoice::Bolt11Invoice; use tokio::sync::mpsc; @@ -10,6 +10,7 @@ pub enum UICoreMsg { Send(Bolt11Invoice), Receive(u64), AddFederation(InviteCode), + Unlock(String), } #[derive(Debug, Clone)] @@ -23,6 +24,9 @@ pub enum CoreUIMsg { ReceiveFailed(String), BalanceUpdated(Amount), AddFederationFailed(String), + Unlocking, + UnlockSuccess, + UnlockFailed(String), } #[derive(Debug)] @@ -52,6 +56,10 @@ impl UIHandle { pub async fn receive(&self, amount: u64) { self.msg_send(UICoreMsg::Receive(amount)).await; } + + pub async fn unlock(&self, password: String) { + self.msg_send(UICoreMsg::Unlock(password)).await; + } } impl CoreHandle { diff --git a/src/components/button.rs b/src/components/button.rs index e07e923..343efef 100644 --- a/src/components/button.rs +++ b/src/components/button.rs @@ -8,35 +8,7 @@ use iced::{ use crate::{Message, Route}; -use super::{darken, lighten}; - -pub enum SvgIcon { - ChevronDown, - DownLeft, - Heart, - Home, - LeftRight, - People, - Settings, - Squirrel, - UpRight, - Copy, -} - -fn map_icon(icon: SvgIcon) -> Svg<'static, Theme> { - match icon { - SvgIcon::ChevronDown => Svg::from_path("assets/icons/chevron_down.svg"), - SvgIcon::DownLeft => Svg::from_path("assets/icons/down_left.svg"), - SvgIcon::Heart => Svg::from_path("assets/icons/heart.svg"), - SvgIcon::Home => Svg::from_path("assets/icons/home.svg"), - SvgIcon::LeftRight => Svg::from_path("assets/icons/left_right.svg"), - SvgIcon::People => Svg::from_path("assets/icons/people.svg"), - SvgIcon::Settings => Svg::from_path("assets/icons/settings.svg"), - SvgIcon::Squirrel => Svg::from_path("assets/icons/squirrel.svg"), - SvgIcon::UpRight => Svg::from_path("assets/icons/up_right.svg"), - SvgIcon::Copy => Svg::from_path("assets/icons/copy.svg"), - } -} +use super::{darken, lighten, map_icon, SvgIcon}; pub fn h_button(text_str: &str, icon: SvgIcon) -> Button<'_, Message, Theme> { let svg: Svg<'_, Theme> = map_icon(icon); diff --git a/src/components/icon.rs b/src/components/icon.rs new file mode 100644 index 0000000..a75ced3 --- /dev/null +++ b/src/components/icon.rs @@ -0,0 +1,29 @@ +use iced::{widget::Svg, Theme}; + +pub enum SvgIcon { + ChevronDown, + DownLeft, + Heart, + Home, + LeftRight, + People, + Settings, + Squirrel, + UpRight, + Copy, +} + +pub fn map_icon(icon: SvgIcon) -> Svg<'static, Theme> { + match icon { + SvgIcon::ChevronDown => Svg::from_path("assets/icons/chevron_down.svg"), + SvgIcon::DownLeft => Svg::from_path("assets/icons/down_left.svg"), + SvgIcon::Heart => Svg::from_path("assets/icons/heart.svg"), + SvgIcon::Home => Svg::from_path("assets/icons/home.svg"), + SvgIcon::LeftRight => Svg::from_path("assets/icons/left_right.svg"), + SvgIcon::People => Svg::from_path("assets/icons/people.svg"), + SvgIcon::Settings => Svg::from_path("assets/icons/settings.svg"), + SvgIcon::Squirrel => Svg::from_path("assets/icons/squirrel.svg"), + SvgIcon::UpRight => Svg::from_path("assets/icons/up_right.svg"), + SvgIcon::Copy => Svg::from_path("assets/icons/copy.svg"), + } +} diff --git a/src/components/mod.rs b/src/components/mod.rs index 3992b17..6bfaa5e 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -6,3 +6,6 @@ pub use util::*; mod sidebar; pub use sidebar::*; + +mod icon; +pub use icon::*; diff --git a/src/core.rs b/src/core.rs index 4d4f2bd..e1d0efc 100644 --- a/src/core.rs +++ b/src/core.rs @@ -282,6 +282,15 @@ pub fn run_core() -> Subscription { .await; } } + // TODO: actually use this to unlock + UICoreMsg::Unlock(_password) => { + core.msg(CoreUIMsg::Unlocking).await; + // if let Err(e) = core.unlock(password).await { + // error!("Error unlocking: {e}"); + // core.msg(CoreUIMsg::UnlockFailed(e.to_string())).await; + // } + core.msg(CoreUIMsg::UnlockSuccess).await; + } } } } diff --git a/src/main.rs b/src/main.rs index e488ab2..d219d7c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,8 +8,8 @@ use std::sync::Arc; use bridge::CoreUIMsg; use iced::subscription::Subscription; use iced::widget::row; +use iced::Element; use iced::{clipboard, program, Color}; -use iced::{Alignment, Element}; use iced::{Command, Font}; pub mod bridge; @@ -50,6 +50,9 @@ pub struct HarborWallet { send_status: SendStatus, send_failure_reason: Option, send_input_str: String, + password_input_str: String, + unlock_status: UnlockStatus, + unlock_failure_reason: Option, receive_failure_reason: Option, receive_status: ReceiveStatus, receive_amount_str: String, @@ -77,6 +80,14 @@ enum ReceiveStatus { WaitingToReceive, } +#[derive(Default, Debug, Clone)] +enum UnlockStatus { + #[default] + Locked, + Unlocked, + Unlocking, +} + #[derive(Debug, Clone)] pub enum Message { // Setup @@ -86,12 +97,14 @@ pub enum Message { TransferAmountChanged(String), ReceiveAmountChanged(String), SendInputChanged(String), + PasswordInputChanged(String), CopyToClipboard(String), // Async commands we fire from the UI to core Noop, Send(String), Receive(u64), GenerateInvoice, + Unlock(String), // Core messages we get from core CoreMessage(CoreUIMsg), } @@ -101,12 +114,15 @@ impl HarborWallet { Self { ui_handle: None, balance: Amount::ZERO, - active_route: Route::Home, + active_route: Route::Unlock, transfer_amount_str: String::new(), receive_amount_str: String::new(), send_input_str: String::new(), send_status: SendStatus::Idle, send_failure_reason: 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, @@ -145,6 +161,14 @@ impl HarborWallet { } } + async fn async_unlock(ui_handle: Option>, password: String) { + if let Some(ui_handle) = ui_handle { + ui_handle.clone().unlock(password).await; + } else { + panic!("UI handle is None"); + } + } + fn update(&mut self, message: Message) -> Command { match message { // Setup @@ -170,6 +194,10 @@ impl HarborWallet { self.send_input_str = input; Command::none() } + Message::PasswordInputChanged(input) => { + self.password_input_str = input; + Command::none() + } // Async commands we fire from the UI to core Message::Noop => Command::none(), Message::Send(invoice_str) => match self.send_status { @@ -213,6 +241,15 @@ impl HarborWallet { } } }, + Message::Unlock(password) => match self.unlock_status { + UnlockStatus::Unlocking => Command::none(), + _ => { + self.unlock_failure_reason = None; + Command::perform(Self::async_unlock(self.ui_handle.clone(), password), |_| { + Message::Noop + }) + } + }, Message::CopyToClipboard(s) => { println!("Copying to clipboard: {s}"); clipboard::write(s) @@ -257,6 +294,20 @@ impl HarborWallet { // todo show error Command::none() } + CoreUIMsg::Unlocking => { + self.unlock_status = UnlockStatus::Unlocking; + Command::none() + } + CoreUIMsg::UnlockSuccess => { + self.unlock_status = UnlockStatus::Unlocked; + self.active_route = Route::Home; + Command::none() + } + CoreUIMsg::UnlockFailed(reason) => { + self.unlock_status = UnlockStatus::Locked; + self.unlock_failure_reason = Some(reason); + Command::none() + } }, } } @@ -265,17 +316,14 @@ impl HarborWallet { let sidebar = crate::components::sidebar(self); let active_route = match self.active_route { - Route::Home => crate::routes::home(self), - Route::Mints => crate::routes::mints(self), - Route::Transfer => crate::routes::transfer(self), - Route::Receive => crate::routes::receive(self), - Route::Send => crate::routes::send(self), - _ => crate::routes::home(self), + Route::Unlock => crate::routes::unlock(self), + Route::Home => row![sidebar, crate::routes::home(self)].into(), + Route::Receive => row![sidebar, crate::routes::receive(self)].into(), + Route::Send => row![sidebar, crate::routes::send(self)].into(), + _ => row![crate::routes::home(self)].into(), }; - row![sidebar, active_route] - .align_items(Alignment::Center) - .into() + active_route } fn theme(&self) -> iced::Theme { diff --git a/src/routes/mod.rs b/src/routes/mod.rs index 41a3bfb..2f09655 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -13,9 +13,13 @@ pub use receive::*; pub mod send; pub use send::*; +pub mod unlock; +pub use unlock::*; + #[derive(Default, PartialEq, Debug, Clone, Copy)] pub enum Route { #[default] + Unlock, Home, Mints, Transfer, diff --git a/src/routes/unlock.rs b/src/routes/unlock.rs new file mode 100644 index 0000000..11f67f5 --- /dev/null +++ b/src/routes/unlock.rs @@ -0,0 +1,27 @@ +use crate::components::{h_button, SvgIcon}; +use iced::widget::{center, column, container, text_input, Svg}; +use iced::{Alignment, Element, Length}; + +use crate::{HarborWallet, Message}; + +pub fn unlock(harbor: &HarborWallet) -> Element { + // let receive_button = h_button("Receive", SvgIcon::DownLeft).on_press(Message::Receive(100)); + let unlock_button = h_button("Unlock", SvgIcon::DownLeft) + .on_press(Message::Unlock(harbor.password_input_str.clone())) + .width(Length::Fill); + + let password_input = + text_input("password", &harbor.password_input_str).on_input(Message::PasswordInputChanged); + + let harbor_logo = Svg::from_path("assets/harbor_logo.svg") + .width(167) + .height(61); + + container(center( + column![harbor_logo, password_input, unlock_button] + .spacing(32) + .align_items(Alignment::Center) + .width(Length::Fixed(256.)), + )) + .into() +}