Account · Sign in: log in, sign up, providers, a link by email, a second factor

LoginForm takes an email and a password, keeps one signed in on a box,
and signs in on Enter once the address reads and a password is typed;
a failed try says why above Sign in. SignupForm ticks a new password's
rules as they hold and waits for all of them and the terms.
OAuthButtons offer each provider, one spinning while the rest rest.
MagicLinkForm and ForgotPassword send a link to an address and, once
sent, say where it went and offer it again. TwoFactorInput takes a
six-digit code at once and, after one fails, clears the boxes through
forms::PinInput's new attempt, keeping its focus. The Account chapter
splits into sign-in, profile and access, and billing and team.
This commit is contained in:
ZacharyZhang-NY committed 2026-09-27 13:12:49 -04:00
1 parent 69befddeb3
commit c9e3400a71
15 files changed
+1455 -10

No files matched your search

+19 -2
View File
@@ -13,6 +13,7 @@ type OnCode = Rc<dyn Fn(&str, &mut Window, &mut App)>;
struct Pin {
input: Entity<TextInput>,
length: usize,
attempt: usize,
on_complete: Option<OnCode>,
_events: Subscription,
}
@@ -24,6 +25,7 @@ pub struct PinInput {
length: usize,
letters: bool,
masked: bool,
attempt: usize,
on_complete: Option<OnCode>,
}
@@ -35,10 +37,17 @@ impl PinInput {
length,
letters: false,
masked: false,
attempt: 0,
on_complete: None,
}
}
/// Clears the boxes whenever `attempt` changes, so a code that failed makes way for the next.
pub fn attempt(mut self, attempt: usize) -> Self {
self.attempt = attempt;
self
}
/// Accepts letters as well as digits.
pub fn letters(mut self) -> Self {
self.letters = true;
@@ -60,7 +69,7 @@ impl PinInput {
impl RenderOnce for PinInput {
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
let (length, letters) = (self.length, self.letters);
let (length, letters, attempt) = (self.length, self.letters, self.attempt);
let state = window.use_keyed_state(self.id.clone(), cx, |window, cx: &mut Context<Pin>| {
let input = cx.new(|cx| {
TextInput::new(window, cx)
@@ -79,11 +88,19 @@ impl RenderOnce for PinInput {
Pin {
input,
length,
attempt,
on_complete: None,
_events: events,
}
});
state.update(cx, |pin, _| pin.on_complete = self.on_complete.clone());
state.update(cx, |pin, cx| {
pin.on_complete = self.on_complete.clone();
if pin.attempt != attempt {
pin.attempt = attempt;
log::info!("pin input: cleared for attempt {attempt}");
pin.input.update(cx, |field, cx| field.set_text("", cx));
}
});
let input = state.read(cx).input.clone();
let (code, focused, caret_on) = {
let field = input.read(cx);