Skip to content

Commit 3b81212

Browse files
authored
Merge pull request #75 from elsirion/2024-05-menu-overhaul
Clean up menu, select LN or e-cash in a second step
2 parents 45e07ac + d9fb76d commit 3b81212

13 files changed

+208
-17
lines changed

Cargo.lock

+10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ fedimint-mint-client = "0.2.1-rc1"
2525
fedimint-ln-client = "0.2.1-rc1"
2626
futures = "0.3.28"
2727
hex = "0.4.3"
28+
itertools = "0.13.0"
2829
leptos = { version = "0.6.5", features = ["csr"] }
2930
leptos-use = "0.10.2"
3031
leptos-qr-scanner = { git = "https://github.com/elsirion/leptos-qr-scanner", rev = "5830bd6f75d7836189ef1434f71a10222a737a44" }

src/components/app.rs

+1
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ pub fn App() -> impl IntoView {
111111
placeholder="invite code".into()
112112
submit_label="Join".into()
113113
loading=join_action.pending()
114+
default_scan=true
114115
/>
115116
</Show>
116117

src/components/create_wallet.rs

+2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ where
2828
</button>
2929
</Show>
3030
<Show when=move || show_create_wallet_form.get() fallback=|| empty_view() >
31+
<div class="w-full">
3132
<SubmitForm
3233
description="Enter a name for the new wallet".into()
3334
on_submit=move |name| {
@@ -38,6 +39,7 @@ where
3839
submit_label="Create".into()
3940
loading=loading
4041
/>
42+
</div>
4143
</Show>
4244
</div>
4345
}

src/components/joined.rs

+5-13
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use leptos::*;
22

3-
use crate::components::{Balance, ReceiveEcash, ReceiveLn, SendEcash, SendLn, TxList};
3+
use crate::components::{Balance, Receive, Send, TxList};
44
use crate::context::ClientContext;
55

66
//
@@ -36,20 +36,12 @@ pub fn Joined() -> impl IntoView {
3636
view: view! { <TxList update_signal=move || tab_change_signal.get() /> },
3737
},
3838
MenuItem {
39-
title: "Spend".into(),
40-
view: view! { <SendEcash /> },
39+
title: "Send".into(),
40+
view: view! { <Send /> },
4141
},
4242
MenuItem {
43-
title: "Redeem".into(),
44-
view: view! { <ReceiveEcash /> },
45-
},
46-
MenuItem {
47-
title: "LN Send".into(),
48-
view: view! { <SendLn /> },
49-
},
50-
MenuItem {
51-
title: "LN Receive".into(),
52-
view: view! { <ReceiveLn /> },
43+
title: "Receive".into(),
44+
view: view! { <Receive /> },
5345
},
5446
];
5547

src/components/mod.rs

+8
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,13 @@ pub mod ln_receive_form;
99
pub mod loader_icon;
1010
pub mod logo;
1111
pub mod logo_fedimint;
12+
pub mod protocol_selector;
1213
pub mod qrcode;
14+
pub mod receive;
1315
pub mod receive_ecash;
1416
pub mod receive_ln;
17+
pub mod segmented_button;
18+
pub mod send;
1519
pub mod send_ecash;
1620
pub mod send_ln;
1721
pub mod service_worker;
@@ -29,9 +33,13 @@ pub use joined::*;
2933
pub use loader_icon::*;
3034
pub use logo::*;
3135
pub use logo_fedimint::*;
36+
pub use protocol_selector::*;
3237
pub use qrcode::*;
38+
pub use receive::*;
3339
pub use receive_ecash::*;
3440
pub use receive_ln::*;
41+
pub use segmented_button::*;
42+
pub use send::*;
3543
pub use send_ecash::*;
3644
pub use send_ln::*;
3745
pub use submit_button::*;

src/components/protocol_selector.rs

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
use leptos::*;
2+
3+
use crate::components::SegmentedButton;
4+
5+
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
6+
pub enum Protocol {
7+
OnChain,
8+
Lightning,
9+
ECash,
10+
}
11+
12+
impl Protocol {
13+
fn from_idx(idx: usize) -> Protocol {
14+
match idx {
15+
0 => Protocol::OnChain,
16+
1 => Protocol::Lightning,
17+
2 => Protocol::ECash,
18+
_ => panic!("Out of bounds"),
19+
}
20+
}
21+
22+
fn into_idx(self) -> usize {
23+
match self {
24+
Protocol::OnChain => 0,
25+
Protocol::Lightning => 1,
26+
Protocol::ECash => 2,
27+
}
28+
}
29+
}
30+
#[component]
31+
pub fn ProtocolSelector<F>(
32+
#[prop(default = Protocol::Lightning)] active_protocol: Protocol,
33+
on_change: F,
34+
) -> impl IntoView
35+
where
36+
F: Fn(Protocol) + 'static + Copy,
37+
{
38+
view! {
39+
<SegmentedButton
40+
active_idx=active_protocol.into_idx()
41+
segments=vec!["On-Chain".into(), "Lightning".into(), "E-Cash".into()]
42+
on_change=move |idx| on_change(Protocol::from_idx(idx))
43+
/>
44+
}
45+
}

src/components/receive.rs

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
use leptos::*;
2+
3+
use crate::components::{Protocol, ProtocolSelector, ReceiveEcash, ReceiveLn};
4+
5+
#[component]
6+
pub fn Receive() -> impl IntoView {
7+
const DEFAULT_PROTOCOL: Protocol = Protocol::Lightning;
8+
let (active_protocol, set_active_protocol) = create_signal(DEFAULT_PROTOCOL);
9+
10+
let active_protocol_view = move || match active_protocol.get() {
11+
Protocol::OnChain => view! {
12+
"TODO"
13+
}
14+
.into_view(),
15+
Protocol::Lightning => view! {
16+
<ReceiveLn />
17+
}
18+
.into_view(),
19+
Protocol::ECash => view! {
20+
<ReceiveEcash />
21+
}
22+
.into_view(),
23+
};
24+
25+
view! {
26+
<ProtocolSelector
27+
active_protocol=DEFAULT_PROTOCOL
28+
on_change=move |protocol| set_active_protocol.set(protocol)
29+
/>
30+
{active_protocol_view}
31+
}
32+
}

src/components/receive_ecash.rs

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ pub fn ReceiveEcash() -> impl IntoView {
2424
placeholder="e-cash notes".into()
2525
submit_label="Redeem".into()
2626
loading=submit_action.pending()
27+
default_scan=true
2728
/>
2829

2930
{move ||

src/components/segmented_button.rs

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
use itertools::Itertools;
2+
use leptos::*;
3+
4+
#[component]
5+
pub fn SegmentedButton<F>(
6+
#[prop(default = 0)] active_idx: usize,
7+
segments: Vec<String>,
8+
on_change: F,
9+
) -> impl IntoView
10+
where
11+
F: Fn(usize) + 'static + Copy,
12+
{
13+
const ALL_CLASS: &str = "px-4 py-2 focus:outline-none flex-1 text-center";
14+
const FIRST_CLASS: &str = "rounded-l-full";
15+
const LAST_CLASS: &str = "rounded-r-full";
16+
const ACTIVE_CLASS: &str = "text-white bg-blue-500";
17+
const INACTIVE_CLASS: &str = "text-blue-500 bg-transparent";
18+
19+
let (active_idx, set_active_idx) = create_signal(active_idx);
20+
let num_segments = segments.len();
21+
22+
let buttons = segments
23+
.into_iter()
24+
.enumerate()
25+
.map(|(idx, name)| {
26+
let class = move || {
27+
let is_first = idx == 0;
28+
let is_last = idx == num_segments - 1;
29+
let is_active = idx == active_idx.get();
30+
31+
std::iter::once(ALL_CLASS)
32+
.chain(is_first.then(|| FIRST_CLASS))
33+
.chain(is_last.then(|| LAST_CLASS))
34+
.chain(is_active.then(|| ACTIVE_CLASS))
35+
.chain((!is_active).then(|| INACTIVE_CLASS))
36+
.join(" ")
37+
};
38+
view! {
39+
<button
40+
class=class
41+
on:click=move |_| {
42+
set_active_idx.set(idx);
43+
on_change(idx);
44+
}
45+
>
46+
{name}
47+
</button>
48+
}
49+
})
50+
.collect::<Vec<_>>();
51+
52+
view! {
53+
<div class="bg-white p-1 rounded-full shadow flex w-full mb-8">
54+
{buttons}
55+
</div>
56+
}
57+
}

src/components/send.rs

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
use leptos::*;
2+
3+
use crate::components::{Protocol, ProtocolSelector, SendEcash, SendLn};
4+
5+
#[component]
6+
pub fn Send() -> impl IntoView {
7+
const DEFAULT_PROTOCOL: Protocol = Protocol::Lightning;
8+
let (active_protocol, set_active_protocol) = create_signal(DEFAULT_PROTOCOL);
9+
10+
let active_protocol_view = move || match active_protocol.get() {
11+
Protocol::OnChain => view! {
12+
"TODO"
13+
}
14+
.into_view(),
15+
Protocol::Lightning => view! {
16+
<SendLn />
17+
}
18+
.into_view(),
19+
Protocol::ECash => view! {
20+
<SendEcash />
21+
}
22+
.into_view(),
23+
};
24+
25+
view! {
26+
<ProtocolSelector
27+
active_protocol=DEFAULT_PROTOCOL
28+
on_change=move |protocol| set_active_protocol.set(protocol)
29+
/>
30+
{active_protocol_view}
31+
}
32+
}

src/components/send_ln.rs

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ pub fn SendLn() -> impl IntoView {
2424
placeholder="LN invoice".into()
2525
submit_label="Send".into()
2626
loading=submit_action.pending()
27+
default_scan=true
2728
/>
2829

2930

src/components/submit_form.rs

+13-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use leptos::ev::KeyboardEvent;
2+
use leptos::html::Form;
23
use leptos::*;
34
use leptos_qr_scanner::Scan;
5+
use leptos_use::use_element_visibility;
46

57
use crate::components::SubmitButton;
68

@@ -11,6 +13,7 @@ pub fn SubmitForm<F>(
1113
description: String,
1214
submit_label: String,
1315
loading: ReadSignal<bool>,
16+
#[prop(default = false)] default_scan: bool,
1417
) -> impl IntoView
1518
where
1619
F: Fn(String) + 'static + Copy,
@@ -20,7 +23,7 @@ where
2023
let button_is_disabled = Signal::derive(move || loading.get() || value.get().is_empty());
2124
let scan_disabled = Signal::derive(move || loading.get());
2225

23-
let (scan, set_scan) = create_signal(false);
26+
let (scan, set_scan) = create_signal(default_scan);
2427

2528
let textarea = view! {
2629
<textarea
@@ -47,9 +50,12 @@ where
4750
/>
4851
};
4952

53+
let form_ref = create_node_ref::<Form>();
54+
let is_visible = use_element_visibility(form_ref);
55+
5056
let qr_scanner = view! {
5157
<Scan
52-
active=scan
58+
active=Signal::derive(move || scan.get() && is_visible.get())
5359
on_scan=move |invite| {
5460
set_scan.set(false);
5561
set_value.set(invite.clone());
@@ -60,7 +66,10 @@ where
6066
};
6167

6268
view! {
63-
<form on:submit=|ev| ev.prevent_default()>
69+
<form
70+
on:submit=|ev| ev.prevent_default()
71+
node_ref=form_ref
72+
>
6473

6574
<p class="font-body text-gray-600 text-xl">{description}</p>
6675
<Show
@@ -88,7 +97,7 @@ where
8897
}
8998
>{move || {
9099
if scan.get() {
91-
"Stop"
100+
"Manual"
92101
} else {
93102
"Scan"
94103
}

0 commit comments

Comments
 (0)