Skip to content

Commit

Permalink
feat: broken first draft
Browse files Browse the repository at this point in the history
  • Loading branch information
tvolk131 committed Oct 9, 2024
1 parent cea366b commit 4e3c37b
Show file tree
Hide file tree
Showing 10 changed files with 620 additions and 450 deletions.
6 changes: 3 additions & 3 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::sync::Arc;

use iced::{
futures::StreamExt,
widget::{column, container, row, scrollable, stack},
widget::{column, container, row, stack},
Element, Length, Task,
};
use nip_55::nip_46::{Nip46OverNip55ServerStream, Nip46RequestApproval};
Expand Down Expand Up @@ -154,9 +154,9 @@ impl App {
pub fn view(&self) -> Element<Message> {
let Self { page, .. } = self;

let mut content: Element<Message> = Element::new(scrollable(
let mut content: Element<Message> = Element::new(
container(column![page.view()].spacing(20).padding(20)).center_x(Length::Fill),
));
);

if page.to_name() != RouteName::Unlock {
content = Element::new(row![sidebar(self), content]);
Expand Down
492 changes: 49 additions & 443 deletions src/routes/bitcoin_wallet.rs

Large diffs are not rendered by default.

83 changes: 83 additions & 0 deletions src/routes/bitcoin_wallet/federations.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
use iced::{widget::Column, Task};

use crate::{app, fedimint::FederationView, routes::ConnectedState};

pub mod add;
pub mod federation_details;
pub mod list;

#[derive(Debug, Clone)]
pub enum Message {
Add(add::Message),
}

pub struct Page {
pub connected_state: ConnectedState,
pub subroute: Subroute,
}

impl Page {
pub fn new(connected_state: &ConnectedState, subroute: Subroute) -> Self {
Self {
connected_state: connected_state.clone(),
subroute: Subroute::List(list::Page::new(connected_state)),
}
}

pub fn update(&mut self, msg: Message) -> Task<app::Message> {
match msg {
Message::Add(add_msg) => {
if let Subroute::Add(add_page) = &mut self.subroute {
add_page.update(add_msg)
} else {
Task::none()
}
}
}
}

pub fn view<'a>(&self) -> Column<'a, app::Message> {
match &self.subroute {
Subroute::List(list) => list.view(),
Subroute::Add(add) => add.view(),
Subroute::FederationDetails(federation_details) => federation_details.view(),
}
}
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SubrouteName {
List,
Add,
FederationDetails(FederationView),
}

impl SubrouteName {
pub fn to_default_subroute(&self, connected_state: &ConnectedState) -> Subroute {
match self {
Self::List => Subroute::List(list::Page::new(connected_state)),
Self::FederationDetails(federation_view) => {
Subroute::FederationDetails(federation_details::Page::new(federation_view.clone()))
}
Self::Add => Subroute::Add(add::Page::new(connected_state)),
}
}
}

pub enum Subroute {
List(list::Page),
Add(add::Page),
FederationDetails(federation_details::Page),
}

impl Subroute {
pub fn to_name(&self) -> SubrouteName {
match self {
Self::List(_) => SubrouteName::List,
Self::Add(_) => SubrouteName::Add,
Self::FederationDetails(federation_details) => {
SubrouteName::FederationDetails(federation_details.federation_view().clone())
}
}
}
}
273 changes: 273 additions & 0 deletions src/routes/bitcoin_wallet/federations/add.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
use std::str::FromStr;

use fedimint_core::{
config::{ClientConfig, META_FEDERATION_NAME_KEY},
invite_code::InviteCode,
};
use iced::{
widget::{text_input, Column, Text},
Task,
};

use crate::{
app,
routes::{self, bitcoin_wallet, container, ConnectedState, Loadable, RouteName},
ui_components::{icon_button, PaletteColor, SvgIcon, Toast, ToastStatus},
util::truncate_text,
};

#[derive(Debug, Clone)]
pub enum Message {
JoinFederationInviteCodeInputChanged(String),

LoadedFederationConfigFromInviteCode {
// The invite code that was used to load the federation config.
config_invite_code: InviteCode,
// The loaded federation config.
config: ClientConfig,
},
FailedToLoadFederationConfigFromInviteCode {
// The invite code that was used to attempt to load the federation config.
config_invite_code: InviteCode,
},

JoinFederation(InviteCode),
JoinedFederation(InviteCode),
}

pub struct Page {
connected_state: ConnectedState,
federation_invite_code: String,
parsed_federation_invite_code_state_or: Option<ParsedFederationInviteCodeState>,
}

pub struct ParsedFederationInviteCodeState {
invite_code: InviteCode,
loadable_federation_config: Loadable<ClientConfig>,
}

impl Page {
pub fn new(connected_state: &ConnectedState) -> Self {
Self {
connected_state: connected_state.clone(),
federation_invite_code: String::new(),
parsed_federation_invite_code_state_or: None,
}
}

pub fn update(&mut self, msg: Message) -> Task<app::Message> {
match msg {
Message::JoinFederationInviteCodeInputChanged(new_federation_invite_code) => {
self.federation_invite_code = new_federation_invite_code;

if let Ok(invite_code) = InviteCode::from_str(&self.federation_invite_code) {
self.parsed_federation_invite_code_state_or =
Some(ParsedFederationInviteCodeState {
invite_code: invite_code.clone(),
loadable_federation_config: Loadable::Loading,
});

Task::perform(
async move {
match fedimint_api_client::download_from_invite_code(&invite_code).await
{
Ok(config) => {
app::Message::Routes(routes::Message::BitcoinWalletPage(
bitcoin_wallet::Message::Federations(super::Message::Add(
Message::LoadedFederationConfigFromInviteCode {
config_invite_code: invite_code,
config,
},
)),
))
}
// TODO: Include error in message and display it in the UI.
Err(_err) => {
app::Message::Routes(routes::Message::BitcoinWalletPage(
bitcoin_wallet::Message::Federations(super::Message::Add(
Message::FailedToLoadFederationConfigFromInviteCode {
config_invite_code: invite_code,
},
)),
))
}
}
},
|msg| msg,
)
} else {
self.parsed_federation_invite_code_state_or = None;

Task::none()
}
}
Message::LoadedFederationConfigFromInviteCode {
config_invite_code,
config,
} => {
if let Some(ParsedFederationInviteCodeState {
invite_code,
loadable_federation_config,
}) = &mut self.parsed_federation_invite_code_state_or
{
// If the invite code has changed since the request was made, ignore the response.
if &config_invite_code == invite_code {
*loadable_federation_config = Loadable::Loaded(config);
}
}

Task::none()
}
Message::FailedToLoadFederationConfigFromInviteCode { config_invite_code } => {
if let Some(ParsedFederationInviteCodeState {
invite_code,
loadable_federation_config,
}) = &mut self.parsed_federation_invite_code_state_or
{
// If the invite code has changed since the request was made, ignore the response.
// Also only update the state if the config hasn't already been loaded.
if &config_invite_code == invite_code
&& matches!(loadable_federation_config, Loadable::Loading)
{
*loadable_federation_config = Loadable::Failed;
}
}

Task::none()
}
Message::JoinFederation(invite_code) => {
let wallet = self.connected_state.wallet.clone();

Task::stream(async_stream::stream! {
match wallet.join_federation(invite_code.clone()).await {
Ok(()) => {
yield app::Message::AddToast(Toast {
title: "Joined federation".to_string(),
body: "You have successfully joined the federation.".to_string(),
status: ToastStatus::Good,
});

yield app::Message::Routes(
routes::Message::BitcoinWalletPage(
bitcoin_wallet::Message::Federations(
super::Message::Add(
Message::JoinedFederation(invite_code)
)
)
)
);
}
Err(err) => {
yield app::Message::AddToast(Toast {
title: "Failed to join federation".to_string(),
body: format!("Failed to join the federation: {err}"),
status: ToastStatus::Bad,
});
}
}
})
}
Message::JoinedFederation(invite_code) => {
// If the invite code matches the one that was just joined, navigate back to the `Main` page.
if let Some(invite_code_state) = &self.parsed_federation_invite_code_state_or {
if invite_code_state.invite_code == invite_code {
return Task::done(app::Message::Routes(routes::Message::Navigate(
RouteName::BitcoinWallet(bitcoin_wallet::SubrouteName::Main),
)));
}
}

Task::none()
}
}
}

pub fn view<'a>(&self) -> Column<'a, app::Message> {
let mut container = container("Join Federation")
.push(
text_input("Federation Invite Code", &self.federation_invite_code)
.on_input(|input| {
app::Message::Routes(routes::Message::BitcoinWalletPage(
bitcoin_wallet::Message::Federations(super::Message::Add(
Message::JoinFederationInviteCodeInputChanged(input),
)),
))
})
.padding(10)
.size(30),
)
.push(
icon_button("Join Federation", SvgIcon::Groups, PaletteColor::Primary)
.on_press_maybe(self.parsed_federation_invite_code_state_or.as_ref().map(
|parsed_federation_invite_code_state| {
app::Message::Routes(routes::Message::BitcoinWalletPage(
bitcoin_wallet::Message::Federations(super::Message::Add(
Message::JoinFederation(
parsed_federation_invite_code_state.invite_code.clone(),
),
)),
))
},
)),
);

if let Some(parsed_federation_invite_code_state) =
&self.parsed_federation_invite_code_state_or
{
container = container
.push(Text::new("Federation ID").size(25))
.push(Text::new(truncate_text(
&parsed_federation_invite_code_state
.invite_code
.federation_id()
.to_string(),
21,
true,
)));

match &parsed_federation_invite_code_state.loadable_federation_config {
Loadable::Loading => {
container = container.push(Text::new("Loading..."));
}
Loadable::Loaded(client_config) => {
container = container
.push(Text::new("Federation Name").size(25))
.push(Text::new(
client_config
.meta::<String>(META_FEDERATION_NAME_KEY)
.ok()
.flatten()
.unwrap_or_default(),
))
.push(Text::new("Modules").size(25))
.push(Text::new(
client_config
.modules
.values()
.map(|module| module.kind().to_string())
.collect::<Vec<_>>()
.join(", "),
))
.push(Text::new("Guardians").size(25));
for peer_url in client_config.global.api_endpoints.values() {
container = container
.push(Text::new(format!("{} ({})", peer_url.name, peer_url.url)));
}
}
Loadable::Failed => {
container = container.push(Text::new("Failed to load client config"));
}
}
}

container = container.push(
icon_button("Back", SvgIcon::ArrowBack, PaletteColor::Background).on_press(
app::Message::Routes(routes::Message::Navigate(RouteName::BitcoinWallet(
bitcoin_wallet::SubrouteName::Main,
))),
),
);

container
}
}
Loading

0 comments on commit 4e3c37b

Please sign in to comment.