Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

task(recovery): Redo seed enter recovery page #1915

Merged
merged 12 commits into from
Mar 12, 2024
7 changes: 6 additions & 1 deletion kit/src/elements/input/mod.rs
Original file line number Diff line number Diff line change
@@ -9,7 +9,6 @@ use uuid::Uuid;
pub type ValidationError = String;
use crate::elements::label::Label;
use crate::elements::loader::Loader;

use common::icons::outline::Shape as Icon;
use common::icons::Icon as IconElement;

@@ -151,6 +150,7 @@ pub struct Props<'a> {
select_on_focus: Option<bool>,
onchange: Option<EventHandler<'a, (String, bool)>>,
onreturn: Option<EventHandler<'a, (String, bool, Code)>>,
onfocus: Option<EventHandler<'a, ()>>,
reset: Option<UseState<bool>>,
#[props(default = false)]
disable_onblur: bool,
@@ -416,6 +416,11 @@ pub fn Input<'a>(cx: Scope<'a, Props<'a>>) -> Element<'a> {
maxlength: "{max_length}",
"type": "{typ}",
placeholder: "{cx.props.placeholder}",
onfocus: move |_| {
if let Some(e) = &cx.props.onfocus {
e.call(())
}
},
onblur: move |_| {
if onblur_active {
emit_return(&cx, val.read().to_string(), *valid.current(), Code::Enter);
4 changes: 2 additions & 2 deletions ui/src/components/settings/sub_pages/profile/mod.rs
Original file line number Diff line number Diff line change
@@ -639,7 +639,7 @@ pub fn ProfileSettings(cx: Scope) -> Element {
class: "col",
span {
aria_label: "seed-word-number-{((idx * 2) + 1).to_string()}",
class: "num", ((idx * 2) + 1).to_string()
class: "num disable-select", ((idx * 2) + 1).to_string()
},
span {
aria_label: "seed-word-value-{((idx * 2) + 1).to_string()}",
@@ -650,7 +650,7 @@ pub fn ProfileSettings(cx: Scope) -> Element {
class: "col",
span {
aria_label: "seed-word-number-{((idx * 2) + 2).to_string()}",
class: "num", ((idx * 2) + 2).to_string()
class: "num disable-select", ((idx * 2) + 2).to_string()
},
span {
aria_label: "seed-word-value-{((idx * 2) + 2).to_string()}",
5 changes: 2 additions & 3 deletions ui/src/layouts/log_in/copy_seed_words.rs
Original file line number Diff line number Diff line change
@@ -66,10 +66,9 @@ fn SeedWords(cx: Scope, page: UseState<AuthPages>, words: Vec<String>) -> Elemen
class: "row",
div {
class: "col",

span {
aria_label: "seed-word-number-{((idx * 2) + 1).to_string()}",
class: "num", ((idx * 2) + 1).to_string()
class: "num disable-select", ((idx * 2) + 1).to_string()
},
span {
aria_label: "seed-word-value-{((idx * 2) + 1).to_string()}",
@@ -80,7 +79,7 @@ fn SeedWords(cx: Scope, page: UseState<AuthPages>, words: Vec<String>) -> Elemen
class: "col",
span {
aria_label: "seed-word-number-{((idx * 2) + 2).to_string()}",
class: "num", ((idx * 2) + 2).to_string()
class: "num disable-select", ((idx * 2) + 2).to_string()
},
span {
aria_label: "seed-word-value-{((idx * 2) + 2).to_string()}",
9 changes: 9 additions & 0 deletions ui/src/layouts/log_in/enter_seed_handler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
let page = document.getElementById("enter-seed-words-layout");
let inputs = page.getElementsByTagName("input");
for (let input of inputs) {
input.addEventListener("paste", event => {
event.preventDefault();
let paste = (event.clipboardData || window.clipboardData).getData("text");
dioxus.send(paste)
})
}
150 changes: 124 additions & 26 deletions ui/src/layouts/log_in/enter_seed_words.rs
Original file line number Diff line number Diff line change
@@ -8,7 +8,12 @@ use common::{
use dioxus::prelude::*;
use dioxus_desktop::{use_window, LogicalSize};
use futures::{channel::oneshot, StreamExt};
use kit::elements::{button::Button, input, label::Label, Appearance};
use kit::elements::{
button::Button,
input::{self, Options},
label::Label,
Appearance,
};

use crate::get_app_style;

@@ -38,17 +43,41 @@ struct Cmd {
pub fn Layout(cx: Scope, pin: UseRef<String>, page: UseState<AuthPages>) -> Element {
let state = use_ref(cx, State::load);
let loading = use_state(cx, || false);
let input = use_ref(cx, String::new);
let input: &UseRef<Vec<_>> = use_ref(cx, || (0..12).map(|_| String::new()).collect());
let seed_error = use_state(cx, || None);
let focus = use_ref(cx, || 0);

let window = use_window(cx);

if !matches!(&*page.current(), AuthPages::Success(_)) {
window.set_inner_size(LogicalSize {
width: 500.0,
height: 280.0,
height: 440.0,
});
}

let eval = use_eval(cx);
use_effect(cx, (), move |_| {
to_owned![eval, input];
async move {
if let Ok(eval) = eval(include_str!("./enter_seed_handler.js")) {
loop {
if let Ok(val) = eval.recv().await {
let paste = val.to_string();
let paste = &paste[1..(paste.len() - 1)]; // Trim the apostrophes from the input
if !paste.is_empty() {
let phrases = paste.split("\\n").collect::<Vec<_>>();
for i in 0..12 {
if i < phrases.len() {
input.with_mut(|v: &mut Vec<String>| v[i] = phrases[i].into());
}
}
}
}
}
}
}
});
// todo: show toasts to inform user of errors.
let ch = use_coroutine(cx, |mut rx: UnboundedReceiver<Cmd>| {
to_owned![loading, page, seed_error];
@@ -109,28 +138,97 @@ pub fn Layout(cx: Scope, pin: UseRef<String>, page: UseState<AuthPages>) -> Elem
aria_label: "instructions",
get_local_text("enter-seed-words.instructions")
},
input::Input {
aria_label: "recovery-seed-input".into(),
focus: true,
placeholder: get_local_text("enter-seed-words.placeholder"),
onchange: move |(x, is_valid): (String, bool)| {
if x.is_empty() || seed_error.get().is_some() {
seed_error.set(None);
}
if is_valid {
*input.write_silent() = x;
} else{
seed_error.set(Some(SeedError::ValidationError));
}
},
onreturn: move |_|{
loading.set(true);
ch.send(Cmd {
seed_words: input.read().clone(),
passphrase: pin.read().clone()
});
}
},
div {
class: "seed-words",
(0..6).map(|idx|{
let idx = idx * 2;
let other = idx + 1;
rsx!(div {
class: "row",
div {
class: "col",
span {
aria_label: "seed-word-number-{(idx + 1).to_string()}",
class: "num disable-select", (idx + 1).to_string()
},
input::Input {
aria_label: "recovery-seed-input".into(),
value: input.read()[idx].clone(),
select_on_focus: *focus.read() == idx,
focus: *focus.read() == idx, // select class gets removed on focus. this forces an update
placeholder: "".into(),
disable_onblur: true,
options: Options {
clear_on_submit: false,
..Default::default()
},
onfocus: move |_|{
*focus.write() = idx;
},
onchange: move |(x, is_valid): (String, bool)| {
if x.is_empty() || seed_error.get().is_some() {
seed_error.set(None);
}
if is_valid {
input.with_mut(|v|v[idx] = x);
} else{
seed_error.set(Some(SeedError::ValidationError));
}
},
onreturn: move |_| {
let f = *focus.read();
*focus.write() = (f + 1) % 12;
}
},
},
div {
class: "col",
span {
aria_label: "seed-word-number-{(other + 1).to_string()}",
class: "num disable-select", (other + 1).to_string()
},
input::Input {
aria_label: "recovery-seed-input".into(),
value: input.read()[other].clone(),
focus: *focus.read() == other,
select_on_focus: *focus.read() == other, // select class gets removed on focus. this forces an update
placeholder: "".into(),
disable_onblur: true,
options: Options {
clear_on_submit: false,
..Default::default()
},
onfocus: move |_|{
*focus.write() = other;
},
onchange: move |(x, is_valid): (String, bool)| {
if x.is_empty() || seed_error.get().is_some() {
seed_error.set(None);
}
if is_valid {
input.with_mut(|v|v[other] = x);
} else{
seed_error.set(Some(SeedError::ValidationError));
}
},
onreturn: move |_| {
if other == 11 {
loading.set(true);
log::debug!("seed {}", input.read().join(" "));
ch.send(Cmd {
seed_words: input.read().join(" ").clone(),
passphrase: pin.read().clone()
});
} else {
let f = *focus.read();
*focus.write() = (f + 1) % 12;
}
}
},
}
})
})
}
seed_error.as_ref().map(|e| rsx!(
span {
aria_label: "input-error",
@@ -155,7 +253,7 @@ pub fn Layout(cx: Scope, pin: UseRef<String>, page: UseState<AuthPages>) -> Elem
onpress: move |_| {
loading.set(true);
ch.send(Cmd {
seed_words: input.read().clone(),
seed_words: input.read().join(" ").clone(),
passphrase: pin.read().clone()
});
}
4 changes: 4 additions & 0 deletions ui/src/layouts/style.scss
Original file line number Diff line number Diff line change
@@ -263,6 +263,10 @@
flex: 1;
display: inline-flex;
flex-direction: row;
.input {
background-color: transparent;
min-height: 0;
}
}

.row {