140 lines
4.7 KiB
JavaScript
140 lines
4.7 KiB
JavaScript
// Sign In — magic-link primary, OAuth alternatives. Default action is
|
|
// "Send me a magic link" (no passwords — fits the "no homework" brand).
|
|
// On submit, transitions to a "Check your inbox" confirmation state.
|
|
|
|
function SignIn() {
|
|
const [email, setEmail] = React.useState("");
|
|
const [submitting, setSubmitting] = React.useState(false);
|
|
const [sent, setSent] = React.useState(false);
|
|
|
|
const valid = /\S+@\S+\.\S+/.test(email);
|
|
|
|
const handleSubmit = (e) => {
|
|
e.preventDefault();
|
|
if (!valid || submitting) return;
|
|
setSubmitting(true);
|
|
setTimeout(() => {
|
|
setSubmitting(false);
|
|
setSent(true);
|
|
}, 700);
|
|
};
|
|
|
|
return (
|
|
<div className="page">
|
|
<TopBar rightLink={{ href: "index.html", label: "Back to home" }} />
|
|
|
|
<main className="auth-main">
|
|
<Glows />
|
|
|
|
<div className="auth-card">
|
|
{sent ? (
|
|
<SentConfirmation email={email} onChangeEmail={() => setSent(false)} />
|
|
) : (
|
|
<>
|
|
<div className="auth-eye">Welcome back</div>
|
|
<h1 className="auth-title">
|
|
Sign in and <em>keep building</em>.
|
|
</h1>
|
|
<p className="auth-sub">
|
|
We'll email you a one-tap link. No passwords to remember, no homework.
|
|
</p>
|
|
|
|
<form className="auth-form" onSubmit={handleSubmit} noValidate>
|
|
<div className="auth-field">
|
|
<label className="auth-label" htmlFor="email">Email</label>
|
|
<input
|
|
id="email" type="email" autoComplete="email" required autoFocus
|
|
className="auth-input"
|
|
placeholder="you@somewhere.com"
|
|
value={email}
|
|
onChange={(e) => setEmail(e.target.value)}
|
|
/>
|
|
</div>
|
|
<button type="submit" disabled={!valid || submitting}
|
|
className="auth-btn auth-btn-primary">
|
|
{submitting ? (
|
|
<><span className="auth-spinner" /> Sending…</>
|
|
) : (
|
|
<><MailIcon size={17} /> Send me a magic link</>
|
|
)}
|
|
</button>
|
|
</form>
|
|
|
|
<div className="auth-divider">or continue with</div>
|
|
|
|
<div className="auth-oauth">
|
|
<button type="button" className="auth-btn auth-btn-ghost">
|
|
<GoogleIcon /> Continue with Google
|
|
</button>
|
|
<button type="button" className="auth-btn auth-btn-ghost">
|
|
<AppleIcon /> Continue with Apple
|
|
</button>
|
|
</div>
|
|
|
|
<div className="auth-foot">
|
|
Don't have an invite yet? <a href="Beta Signup.html">Request one →</a>
|
|
</div>
|
|
</>
|
|
)}
|
|
</div>
|
|
|
|
<TrustStrip items={["No passwords", "No homework", "🇨🇦 Built in Canada"]} />
|
|
</main>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// Confirmation: "Check your inbox at you@x.com" with a resend timer + the
|
|
// option to change email and try again.
|
|
function SentConfirmation({ email, onChangeEmail }) {
|
|
const [left, restart] = useResendTimer(30);
|
|
|
|
return (
|
|
<div className="auth-success">
|
|
<div className="auth-success-badge">
|
|
<MailIcon size={26} />
|
|
</div>
|
|
<div className="auth-eye">Check your inbox</div>
|
|
<h1 className="auth-title" style={{ marginTop: 10 }}>
|
|
Magic link <em>sent</em>.
|
|
</h1>
|
|
<p className="auth-sub">
|
|
We just sent a one-tap sign-in link to
|
|
<span className="email-chip">{email}</span>.
|
|
Tap it on this device to keep building.
|
|
</p>
|
|
|
|
<div className="auth-tip">
|
|
<span className="auth-tip-icon">
|
|
<svg width="13" height="13" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" aria-hidden="true">
|
|
<circle cx="8" cy="8" r="6.5"/>
|
|
<path d="M8 5v4M8 11v.5"/>
|
|
</svg>
|
|
</span>
|
|
<span>
|
|
Can't find it? Check your <b style={{ color: "var(--fg)", fontWeight: 500 }}>spam folder</b> or wait a few seconds —
|
|
email is slower than Vibn.
|
|
</span>
|
|
</div>
|
|
|
|
<div className="auth-resend">
|
|
Didn't get it?{" "}
|
|
{left > 0 ? (
|
|
<button type="button" disabled>Resend in {left}s</button>
|
|
) : (
|
|
<button type="button" onClick={restart}>Send again</button>
|
|
)}
|
|
</div>
|
|
|
|
<div className="auth-foot" style={{ marginTop: 22 }}>
|
|
Wrong email? <button type="button" onClick={onChangeEmail}
|
|
style={{ color: "var(--accent)", fontWeight: 500 }}>
|
|
Use a different one
|
|
</button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
ReactDOM.createRoot(document.getElementById("root")).render(<SignIn />);
|