diff --git a/harbor-ui/assets/icons/arrow_left.svg b/harbor-ui/assets/icons/arrow_left.svg new file mode 100644 index 0000000..2ff42f0 --- /dev/null +++ b/harbor-ui/assets/icons/arrow_left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/harbor-ui/assets/icons/clock.svg b/harbor-ui/assets/icons/clock.svg new file mode 100644 index 0000000..8691d16 --- /dev/null +++ b/harbor-ui/assets/icons/clock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/harbor-ui/assets/icons/eye_closed.svg b/harbor-ui/assets/icons/eye_closed.svg new file mode 100644 index 0000000..6f099a0 --- /dev/null +++ b/harbor-ui/assets/icons/eye_closed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/harbor-ui/src/components/button.rs b/harbor-ui/src/components/button.rs index 5393878..5684c05 100644 --- a/harbor-ui/src/components/button.rs +++ b/harbor-ui/src/components/button.rs @@ -41,9 +41,15 @@ pub fn h_button(text_str: &str, icon: SvgIcon, loading: bool) -> Button<'_, Mess Status::Pressed => darken(Color::BLACK, 0.1), _ => theme.palette().background, }; + + let text_color = match status { + Status::Disabled => gray, + _ => Color::WHITE, + }; + button::Style { background: Some(background.into()), - text_color: Color::WHITE, + text_color, border, shadow: Shadow::default(), } @@ -67,6 +73,8 @@ pub fn sidebar_button( Button::new(content) .style(move |theme, status| { + let gray = lighten(theme.palette().background, 0.5); + let border = Border { color: Color::WHITE, width: 0., @@ -85,9 +93,15 @@ pub fn sidebar_button( (Status::Pressed, false) => darken(bg_color, 0.1), _ => bg_color, }; + + let text_color = match status { + Status::Disabled => gray, + _ => Color::WHITE, + }; + button::Style { background: Some(background.into()), - text_color: Color::WHITE, + text_color, border, shadow: Shadow::default(), } diff --git a/harbor-ui/src/components/federation_item.rs b/harbor-ui/src/components/federation_item.rs index 6bd90c9..2122866 100644 --- a/harbor-ui/src/components/federation_item.rs +++ b/harbor-ui/src/components/federation_item.rs @@ -1,9 +1,9 @@ +use crate::Message; +use harbor_client::db_models::FederationItem; use iced::{ widget::{column, row, text}, Alignment, Element, }; -use harbor_client::db_models::FederationItem; -use crate::Message; use super::{bold_text, h_button, regular_text, subtitle, truncate_text}; use super::{format_amount, map_icon, SvgIcon}; diff --git a/harbor-ui/src/components/icon.rs b/harbor-ui/src/components/icon.rs index 2b1cac8..e6175fc 100644 --- a/harbor-ui/src/components/icon.rs +++ b/harbor-ui/src/components/icon.rs @@ -9,6 +9,7 @@ pub enum SvgIcon { Heart, Home, LeftRight, + ArrowLeft, People, Settings, Squirrel, @@ -21,6 +22,8 @@ pub enum SvgIcon { Bolt, Chain, Eye, + EyeClosed, + Clock, } macro_rules! icon_handle { @@ -51,6 +54,9 @@ pub fn map_icon<'a>(icon: SvgIcon, width: f32, height: f32) -> Svg<'a, Theme> { SvgIcon::Bolt => icon_handle!("bolt.svg"), SvgIcon::Chain => icon_handle!("chain.svg"), SvgIcon::Eye => icon_handle!("eye.svg"), + SvgIcon::EyeClosed => icon_handle!("eye_closed.svg"), + SvgIcon::Clock => icon_handle!("clock.svg"), + SvgIcon::ArrowLeft => icon_handle!("arrow_left.svg"), } .width(width) .height(height) diff --git a/harbor-ui/src/components/indicator.rs b/harbor-ui/src/components/indicator.rs new file mode 100644 index 0000000..bb9e76a --- /dev/null +++ b/harbor-ui/src/components/indicator.rs @@ -0,0 +1,369 @@ +use iced::advanced::{ + layout::{self, Layout}, + overlay::{self, Group}, + renderer, + widget::{Tree, Widget}, + Clipboard, Overlay, Shell, +}; +use iced::widget::container; +use iced::{Element, Event, Length, Point, Rectangle, Size, Vector}; + +/// An element to display a widget over another, controlled by a boolean flag. +/// +/// # Example +/// ```ignore +/// use crate::components::indicator; +/// +/// indicator( +/// "Main content", +/// "Indicator content", +/// indicator::Position::Top, +/// true, // show +/// ) +/// ``` +#[allow(missing_debug_implementations)] +pub struct Indicator<'a, Message, Theme = iced::Theme, Renderer = iced::Renderer> +where + Theme: container::Catalog, + Renderer: iced::advanced::text::Renderer, +{ + content: Element<'a, Message, Theme, Renderer>, + indicator: Element<'a, Message, Theme, Renderer>, + position: Position, + show: bool, + gap: f32, + padding: f32, + snap_within_viewport: bool, + class: Theme::Class<'a>, +} + +impl<'a, Message, Theme, Renderer> Indicator<'a, Message, Theme, Renderer> +where + Theme: container::Catalog, + Renderer: iced::advanced::text::Renderer, +{ + /// The default padding of an [`Indicator`]. + const DEFAULT_PADDING: f32 = 5.0; + + /// Creates a new [`Indicator`]. + pub fn new( + content: impl Into>, + indicator: impl Into>, + position: Position, + show: bool, + ) -> Self { + Indicator { + content: content.into(), + indicator: indicator.into(), + position, + show, + gap: 0.0, + padding: Self::DEFAULT_PADDING, + snap_within_viewport: true, + class: Theme::default(), + } + } + + /// Sets the gap between the content and its [`Indicator`]. + pub fn gap(mut self, gap: impl Into) -> Self { + self.gap = gap.into(); + self + } + + /// Sets the padding of the [`Indicator`]. + pub fn padding(mut self, padding: impl Into) -> Self { + self.padding = padding.into(); + self + } + + /// Sets whether the [`Indicator`] is snapped within the viewport. + pub fn snap_within_viewport(mut self, snap: bool) -> Self { + self.snap_within_viewport = snap; + self + } + + /// Sets the style of the [`Indicator`]. + #[must_use] + pub fn style(mut self, style: impl Fn(&Theme) -> container::Style + 'a) -> Self + where + Theme::Class<'a>: From>, + { + self.class = (Box::new(style) as container::StyleFn<'a, Theme>).into(); + self + } +} + +/// The position of the indicator. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub enum Position { + /// The indicator will appear on the top of the widget. + #[default] + Top, + /// The indicator will appear on the bottom of the widget. + Bottom, + /// The indicator will appear on the left of the widget. + Left, + /// The indicator will appear on the right of the widget. + Right, +} + +impl Widget + for Indicator<'_, Message, Theme, Renderer> +where + Theme: container::Catalog, + Renderer: iced::advanced::text::Renderer, +{ + fn children(&self) -> Vec { + vec![Tree::new(&self.content), Tree::new(&self.indicator)] + } + + fn diff(&self, tree: &mut Tree) { + tree.diff_children(&[self.content.as_widget(), self.indicator.as_widget()]); + } + + fn size(&self) -> Size { + self.content.as_widget().size() + } + + fn layout( + &self, + tree: &mut Tree, + renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + self.content + .as_widget() + .layout(&mut tree.children[0], renderer, limits) + } + + fn on_event( + &mut self, + tree: &mut Tree, + event: Event, + layout: Layout<'_>, + cursor: iced::mouse::Cursor, + renderer: &Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + viewport: &Rectangle, + ) -> iced::event::Status { + self.content.as_widget_mut().on_event( + &mut tree.children[0], + event, + layout, + cursor, + renderer, + clipboard, + shell, + viewport, + ) + } + + fn draw( + &self, + tree: &Tree, + renderer: &mut Renderer, + theme: &Theme, + style: &renderer::Style, + layout: Layout<'_>, + cursor: iced::mouse::Cursor, + viewport: &Rectangle, + ) { + self.content.as_widget().draw( + &tree.children[0], + renderer, + theme, + style, + layout, + cursor, + viewport, + ); + } + + fn overlay<'b>( + &'b mut self, + tree: &'b mut Tree, + layout: Layout<'_>, + renderer: &Renderer, + translation: Vector, + ) -> Option> { + let mut children = tree.children.iter_mut(); + + let content = self.content.as_widget_mut().overlay( + children.next().unwrap(), + layout, + renderer, + translation, + ); + + let indicator = if self.show { + let indicator_overlay = IndicatorContent { + position: layout.position() + translation, + indicator: &self.indicator, + state: children.next().unwrap(), + content_bounds: layout.bounds(), + snap_within_viewport: self.snap_within_viewport, + positioning: self.position, + gap: self.gap, + padding: self.padding, + class: &self.class, + }; + + Some(overlay::Element::new(Box::new(indicator_overlay))) + } else { + None + }; + + if content.is_some() || indicator.is_some() { + Some(Group::with_children(content.into_iter().chain(indicator).collect()).overlay()) + } else { + None + } + } +} + +struct IndicatorContent<'a, 'b, Message, Theme, Renderer> +where + Theme: container::Catalog, + Renderer: iced::advanced::text::Renderer, +{ + position: Point, + indicator: &'b Element<'a, Message, Theme, Renderer>, + state: &'b mut Tree, + content_bounds: Rectangle, + snap_within_viewport: bool, + positioning: Position, + gap: f32, + padding: f32, + class: &'b Theme::Class<'a>, +} + +impl Overlay + for IndicatorContent<'_, '_, Message, Theme, Renderer> +where + Theme: container::Catalog, + Renderer: iced::advanced::text::Renderer, +{ + fn layout(&mut self, renderer: &Renderer, bounds: Size) -> layout::Node { + let viewport = Rectangle::with_size(bounds); + + let indicator_layout = self.indicator.as_widget().layout( + self.state, + renderer, + &layout::Limits::new( + Size::ZERO, + self.snap_within_viewport + .then(|| viewport.size()) + .unwrap_or(Size::INFINITY), + ) + .shrink(iced::Padding::from(self.padding)), + ); + + let bounds = indicator_layout.bounds(); + let x_center = self.position.x + (self.content_bounds.width - bounds.width) / 2.0; + let y_center = self.position.y + (self.content_bounds.height - bounds.height) / 2.0; + + let mut indicator_bounds = { + let offset = match self.positioning { + Position::Top => Vector::new( + x_center, + self.position.y - bounds.height - self.gap - self.padding, + ), + Position::Bottom => Vector::new( + x_center, + self.position.y + self.content_bounds.height + self.gap + self.padding, + ), + Position::Left => Vector::new( + self.position.x - bounds.width - self.gap - self.padding, + y_center, + ), + Position::Right => Vector::new( + self.position.x + self.content_bounds.width + self.gap + self.padding, + y_center, + ), + }; + + Rectangle { + x: offset.x - self.padding, + y: offset.y - self.padding, + width: bounds.width + self.padding * 2.0, + height: bounds.height + self.padding * 2.0, + } + }; + + if self.snap_within_viewport { + if indicator_bounds.x < viewport.x { + indicator_bounds.x = viewport.x; + } else if viewport.x + viewport.width < indicator_bounds.x + indicator_bounds.width { + indicator_bounds.x = viewport.x + viewport.width - indicator_bounds.width; + } + + if indicator_bounds.y < viewport.y { + indicator_bounds.y = viewport.y; + } else if viewport.y + viewport.height < indicator_bounds.y + indicator_bounds.height { + indicator_bounds.y = viewport.y + viewport.height - indicator_bounds.height; + } + } + + layout::Node::with_children( + indicator_bounds.size(), + vec![indicator_layout.translate(Vector::new(self.padding, self.padding))], + ) + .translate(Vector::new(indicator_bounds.x, indicator_bounds.y)) + } + + fn draw( + &self, + renderer: &mut Renderer, + theme: &Theme, + style: &renderer::Style, + layout: Layout<'_>, + cursor: iced::mouse::Cursor, + ) { + let container_style = theme.style(self.class); + + container::draw_background(renderer, &container_style, layout.bounds()); + + let defaults = renderer::Style { + text_color: container_style.text_color.unwrap_or(style.text_color), + }; + + self.indicator.as_widget().draw( + self.state, + renderer, + theme, + &defaults, + layout.children().next().unwrap(), + cursor, + &Rectangle::with_size(Size::INFINITY), + ); + } +} + +impl<'a, Message, Theme, Renderer> From> + for Element<'a, Message, Theme, Renderer> +where + Message: 'a, + Theme: container::Catalog + 'a, + Renderer: iced::advanced::text::Renderer + 'a, +{ + fn from( + indicator: Indicator<'a, Message, Theme, Renderer>, + ) -> Element<'a, Message, Theme, Renderer> { + Element::new(indicator) + } +} + +/// Creates a new [`Indicator`] with the given content and indicator elements. +pub fn indicator<'a, Message, Theme, Renderer>( + content: impl Into>, + indicator: impl Into>, + position: Position, + show: bool, +) -> Indicator<'a, Message, Theme, Renderer> +where + Theme: container::Catalog, + Renderer: iced::advanced::text::Renderer, +{ + Indicator::new(content, indicator, position, show) +} diff --git a/harbor-ui/src/components/input.rs b/harbor-ui/src/components/input.rs index 9608215..bd573c2 100644 --- a/harbor-ui/src/components/input.rs +++ b/harbor-ui/src/components/input.rs @@ -3,7 +3,8 @@ use iced::{ column, row, text, text_input::{self, focus, Id}, TextInput, - }, Background, Border, Color, Element, Task, Theme + }, + Background, Border, Color, Element, Task, Theme, }; use crate::Message; diff --git a/harbor-ui/src/components/mod.rs b/harbor-ui/src/components/mod.rs index fa7a1a0..a685945 100644 --- a/harbor-ui/src/components/mod.rs +++ b/harbor-ui/src/components/mod.rs @@ -51,3 +51,6 @@ pub use colors::*; mod styles; pub use styles::*; + +mod indicator; +pub use indicator::*; diff --git a/harbor-ui/src/components/screen_header.rs b/harbor-ui/src/components/screen_header.rs index 95df8b3..b9dfe3e 100644 --- a/harbor-ui/src/components/screen_header.rs +++ b/harbor-ui/src/components/screen_header.rs @@ -1,19 +1,15 @@ +use crate::{HarborWallet, Message}; +use harbor_client::db_models::FederationItem; use iced::{ widget::{column, row, text}, Alignment, Element, Length, }; -use harbor_client::db_models::FederationItem; -use crate::{HarborWallet, Message}; use super::{format_amount, hr, map_icon, vr, SvgIcon}; pub fn h_screen_header(harbor: &HarborWallet, show_balance: bool) -> Element { if let Some(item) = harbor.active_federation.as_ref() { - let FederationItem { - name, - balance, - .. - } = item; + let FederationItem { name, balance, .. } = item; let people_icon = map_icon(SvgIcon::People, 24., 24.); let current_federation = row![people_icon, text(name).size(24)] .align_y(Alignment::Center) diff --git a/harbor-ui/src/components/sidebar.rs b/harbor-ui/src/components/sidebar.rs index d1e6e82..e8d0832 100644 --- a/harbor-ui/src/components/sidebar.rs +++ b/harbor-ui/src/components/sidebar.rs @@ -1,37 +1,66 @@ +use crate::components::indicator::Position; use crate::components::SvgIcon; use crate::routes::MintSubroute; use iced::widget::container::Style; -use iced::widget::{column, container, vertical_space}; -use iced::Border; +use iced::widget::{column, container, row, text, vertical_space}; use iced::{Alignment, Element, Shadow}; +use iced::{Border, Theme}; use crate::{HarborWallet, Message, Route}; -use super::{harbor_logo, lighten, sidebar_button}; +use super::{harbor_logo, indicator, lighten, map_icon, sidebar_button}; pub fn sidebar(harbor: &HarborWallet) -> Element { + let transfer_disabled = harbor.balance_sats() == 0 || harbor.federation_list.is_empty(); + let transfer_button = sidebar_button( + "Transfer", + SvgIcon::LeftRight, + Route::Transfer, + harbor.active_route, + ); + let add_a_mint_cta = container( + row![ + map_icon(SvgIcon::ArrowLeft, 14., 14.), + text("Add a mint to get started").size(14) + ] + .spacing(8) + .align_y(iced::Alignment::Center), + ) + .padding(8) + .style(|theme: &Theme| container::Style { + text_color: Some(theme.palette().text), + background: Some(theme.palette().primary.into()), + border: Border { + radius: 4.0.into(), + ..Default::default() + }, + ..Default::default() + }); let sidebar = container( column![ harbor_logo(), sidebar_button("Home", SvgIcon::Home, Route::Home, harbor.active_route) .on_press(Message::Navigate(Route::Home)), - sidebar_button( - "Mints", - SvgIcon::People, - Route::Mints(MintSubroute::List), - harbor.active_route - ) - .on_press(Message::Navigate(Route::Mints(MintSubroute::List))), - sidebar_button( - "Transfer", - SvgIcon::LeftRight, - Route::Transfer, - harbor.active_route - ) - .on_press(Message::Navigate(Route::Transfer)), + indicator( + sidebar_button( + "Mints", + SvgIcon::People, + Route::Mints(MintSubroute::List), + harbor.active_route + ) + .on_press(Message::Navigate(Route::Mints(MintSubroute::List))), + add_a_mint_cta, + Position::Right, + harbor.show_add_a_mint_cta + ), + if !transfer_disabled { + transfer_button.on_press(Message::Navigate(Route::Transfer)) + } else { + transfer_button + }, sidebar_button( "History", - SvgIcon::Squirrel, + SvgIcon::Clock, Route::History, harbor.active_route ) diff --git a/harbor-ui/src/main.rs b/harbor-ui/src/main.rs index 634ab4a..6fd170d 100644 --- a/harbor-ui/src/main.rs +++ b/harbor-ui/src/main.rs @@ -1,3 +1,5 @@ +use crate::bridge::run_core; +use crate::components::focus_input_id; use bitcoin::address::NetworkUnchecked; use bitcoin::Address; use components::{Toast, ToastManager, ToastStatus}; @@ -6,6 +8,9 @@ use fedimint_core::core::ModuleKind; use fedimint_core::invite_code::InviteCode; use fedimint_core::Amount; use fedimint_ln_common::lightning_invoice::Bolt11Invoice; +use harbor_client::db_models::transaction_item::TransactionItem; +use harbor_client::db_models::FederationItem; +use harbor_client::{CoreUIMsg, CoreUIMsgPacket, ReceiveSuccessMsg, SendSuccessMsg}; use iced::widget::qr_code::Data; use iced::widget::row; use iced::Element; @@ -19,11 +24,6 @@ use std::collections::HashMap; use std::str::FromStr; use std::sync::Arc; use uuid::Uuid; -use harbor_client::db_models::FederationItem; -use harbor_client::{CoreUIMsg, CoreUIMsgPacket, ReceiveSuccessMsg, SendSuccessMsg}; -use harbor_client::db_models::transaction_item::TransactionItem; -use crate::bridge::run_core; -use crate::components::focus_input_id; pub mod bridge; pub mod components; @@ -192,6 +192,8 @@ pub struct HarborWallet { current_receive_id: Option, peek_status: PeekStatus, add_federation_status: AddFederationStatus, + // Onboarding + show_add_a_mint_cta: bool, } impl HarborWallet { @@ -344,7 +346,14 @@ impl HarborWallet { self.active_route = route; } }, - _ => self.active_route = route, + _ => match route { + Route::Mints(_) => { + // Hide the add a mint cta when navigating to mints + self.show_add_a_mint_cta = false; + self.active_route = route; + } + _ => self.active_route = route, + }, } Task::none() } @@ -575,14 +584,14 @@ impl HarborWallet { let invite = InviteCode::from_str(&invite_code); if let Ok(invite) = invite { let id = Uuid::new_v4(); // todo use this id somewhere - Task::perform( - Self::async_add_federation(self.ui_handle.clone(), id, invite), - |_| Message::Noop, - ) + Task::perform( + Self::async_add_federation(self.ui_handle.clone(), id, invite), + |_| Message::Noop, + ) } else { Task::perform(async {}, move |_| { - Message::AddToast(Toast { - title: "Failed to join mint".to_string(), + Message::AddToast(Toast { + title: "Failed to join mint".to_string(), body: "Invalid invite code".to_string(), status: ToastStatus::Bad, }) @@ -592,14 +601,14 @@ impl HarborWallet { Message::PeekFederation(invite_code) => { let invite = InviteCode::from_str(&invite_code); if let Ok(invite) = invite { - self.peek_status = PeekStatus::Peeking; - let id = Uuid::new_v4(); - Task::perform( - Self::async_peek_federation(self.ui_handle.clone(), id, invite), - |_| Message::Noop, - ) - } else { - Task::perform(async {}, |_| { + self.peek_status = PeekStatus::Peeking; + let id = Uuid::new_v4(); + Task::perform( + Self::async_peek_federation(self.ui_handle.clone(), id, invite), + |_| Message::Noop, + ) + } else { + Task::perform(async {}, |_| { Message::AddToast(Toast { title: "Failed to preview mint".to_string(), body: "Invalid invite code".to_string(), @@ -811,6 +820,9 @@ impl HarborWallet { CoreUIMsg::UnlockSuccess => { self.unlock_status = UnlockStatus::Unlocked; self.active_route = Route::Home; + if self.federation_list.is_empty() { + self.show_add_a_mint_cta = true; + } Task::none() } CoreUIMsg::UnlockFailed(reason) => { diff --git a/harbor-ui/src/routes/history.rs b/harbor-ui/src/routes/history.rs index 9ef1509..506917b 100644 --- a/harbor-ui/src/routes/history.rs +++ b/harbor-ui/src/routes/history.rs @@ -1,4 +1,4 @@ -use iced::widget::column; +use iced::widget::{column, text}; use iced::Element; use crate::components::{basic_layout, h_header, h_screen_header, h_transaction_item, hr}; @@ -7,13 +7,17 @@ use crate::{HarborWallet, Message}; pub fn history(harbor: &HarborWallet) -> Element { let header = h_header("History", "Here's what's happened so far."); - let transactions = harbor - .transaction_history - .iter() - .fold(column![], |column, item| { - column.push(h_transaction_item(item)).push(hr()) - }) - .spacing(16); + let transactions = if harbor.transaction_history.is_empty() { + column![text("Nothing has happened yet.").size(18)] + } else { + harbor + .transaction_history + .iter() + .fold(column![], |column, item| { + column.push(h_transaction_item(item)).push(hr()) + }) + .spacing(16) + }; let column = column![header, transactions].spacing(48); diff --git a/harbor-ui/src/routes/home.rs b/harbor-ui/src/routes/home.rs index 63ba386..a58f651 100644 --- a/harbor-ui/src/routes/home.rs +++ b/harbor-ui/src/routes/home.rs @@ -9,11 +9,24 @@ use super::Route; pub fn home(harbor: &HarborWallet) -> Element { let formatted_balance = format_amount(harbor.balance_sats()); let balance = text(formatted_balance).size(64); - let send_button = - h_button("Send", SvgIcon::UpRight, false).on_press(Message::Navigate(Route::Send)); - let receive_button = - h_button("Deposit", SvgIcon::DownLeft, false).on_press(Message::Navigate(Route::Receive)); - let buttons = row![receive_button, send_button].spacing(32); + let send_disabled = harbor.balance_sats() == 0; + let receive_disabled = harbor.active_federation.is_none(); + let send_button = h_button("Send", SvgIcon::UpRight, false); + let receive_button = h_button("Deposit", SvgIcon::DownLeft, false); + + let buttons = row![ + if !send_disabled { + send_button.on_press(Message::Navigate(Route::Send)) + } else { + send_button + }, + if !receive_disabled { + receive_button.on_press(Message::Navigate(Route::Receive)) + } else { + receive_button + } + ] + .spacing(32); column![ h_screen_header(harbor, false), diff --git a/harbor-ui/src/routes/mints.rs b/harbor-ui/src/routes/mints.rs index 59a35fb..1dce565 100644 --- a/harbor-ui/src/routes/mints.rs +++ b/harbor-ui/src/routes/mints.rs @@ -12,7 +12,10 @@ fn mints_list(harbor: &HarborWallet) -> Element { let list = if harbor.federation_list.is_empty() { column![text("No federations added yet.").size(18)] } else { - let active_federation = harbor.active_federation.as_ref().expect("No active federation"); + let active_federation = harbor + .active_federation + .as_ref() + .expect("No active federation"); harbor .federation_list @@ -47,8 +50,12 @@ fn mints_add(harbor: &HarborWallet) -> Element { None, ); - let peek_mint_button = h_button("Preview", SvgIcon::Eye, harbor.peek_status == PeekStatus::Peeking) - .on_press(Message::PeekFederation(harbor.mint_invite_code_str.clone())); + let peek_mint_button = h_button( + "Preview", + SvgIcon::Eye, + harbor.peek_status == PeekStatus::Peeking, + ) + .on_press(Message::PeekFederation(harbor.mint_invite_code_str.clone())); column![header, mint_input, peek_mint_button].spacing(48) } @@ -59,7 +66,7 @@ fn mints_add(harbor: &HarborWallet) -> Element { let add_mint_button = h_button( "Add Mint", SvgIcon::Plus, - harbor.add_federation_status == AddFederationStatus::Adding + harbor.add_federation_status == AddFederationStatus::Adding, ) .on_press(Message::AddFederation(harbor.mint_invite_code_str.clone())); diff --git a/harbor-ui/src/routes/receive.rs b/harbor-ui/src/routes/receive.rs index c18db21..af47d06 100644 --- a/harbor-ui/src/routes/receive.rs +++ b/harbor-ui/src/routes/receive.rs @@ -1,12 +1,12 @@ -use iced::widget::container::Style; -use iced::widget::{column, container, qr_code, radio, row, text}; -use iced::Color; -use iced::{Border, Element, Font}; -use harbor_client::ReceiveSuccessMsg; use crate::components::{ basic_layout, h_button, h_caption_text, h_header, h_input, h_screen_header, mini_copy, SvgIcon, }; use crate::{HarborWallet, Message, ReceiveMethod, ReceiveStatus}; +use harbor_client::ReceiveSuccessMsg; +use iced::widget::container::Style; +use iced::widget::{column, container, qr_code, radio, row, text}; +use iced::Color; +use iced::{Border, Element, Font}; pub fn receive(harbor: &HarborWallet) -> Element { let receive_string = harbor diff --git a/harbor-ui/src/routes/settings.rs b/harbor-ui/src/routes/settings.rs index 6ad84bd..13577cd 100644 --- a/harbor-ui/src/routes/settings.rs +++ b/harbor-ui/src/routes/settings.rs @@ -23,7 +23,7 @@ pub fn settings(harbor: &HarborWallet) -> Element { let column = match (harbor.settings_show_seed_words, &harbor.seed_words) { (true, Some(s)) => { - let button = h_button("Hide Seed Words", SvgIcon::Squirrel, false) + let button = h_button("Hide Seed Words", SvgIcon::EyeClosed, false) .on_press(Message::ShowSeedWords(false)); let words = text(s).size(24); @@ -34,7 +34,7 @@ pub fn settings(harbor: &HarborWallet) -> Element { column![header, button, words, copy_button] } _ => { - let button = h_button("Show Seed Words", SvgIcon::Squirrel, false) + let button = h_button("Show Seed Words", SvgIcon::Eye, false) .on_press(Message::ShowSeedWords(true)); column![