import { useEffect, useState } from 'react' import { Link, useParams } from 'react-router-dom' import { api } from '../../api/client.js' import PlayerShell from './PlayerShell.jsx' // Public, token-gated confirmation page (/account/verify-email/:token). // // Unauthenticated on purpose: the link arrives in a mailbox and is routinely // opened on a device with no session. That is safe because the token IS the // proof — opening it installs an address on the account it was minted for and // does nothing else. No session is issued here, deliberately: proving control of // a mailbox is not proving control of an account. // // Every failure the server can have — expired, already used, superseded by a // later request, or an address another account confirmed first — comes back as // the same 404. That is not laziness on the server's part; distinguishing them // would let anyone test which addresses have accounts. So this page says the same // thing for all of them, and must keep doing so. export default function VerifyEmail() { const { token } = useParams() const [link, setLink] = useState(null) // { username, email } once validated const [loadErr, setLoadErr] = useState('') const [error, setError] = useState('') const [busy, setBusy] = useState(false) const [done, setDone] = useState(false) useEffect(() => { let active = true api .lookupEmailVerification(token) .then((r) => active && setLink(r || {})) .catch( (err) => active && setLoadErr( err.status === 404 ? 'This confirmation link is invalid or has expired.' : 'Could not load this confirmation link.', ), ) return () => { active = false } }, [token]) async function onConfirm() { setError('') setBusy(true) try { await api.confirmEmailVerification(token) setDone(true) } catch (err) { if (err.status === 404) setError('This confirmation link is no longer usable. Request a new one from your account page.') else if (err.status === 429) setError('Too many attempts. Please try again in a little while.') else setError('Could not confirm your address right now. Please try again later.') setBusy(false) } } // ── Invalid link ─────────────────────────────────────────────────────────── if (loadErr) { return (

{loadErr}

Go to your account

) } if (link === null) { return (
) } // ── Done ─────────────────────────────────────────────────────────────────── if (done) { return (

{link.email ? ( <> {link.email} is now the address for {link.username ? ( <> {' '} {link.username} ) : ( ' your account' )} . ) : ( 'Your email address has been confirmed.' )}

You have not been signed in — confirming an address does not sign you in.

Sign in

) } // ── Confirm ──────────────────────────────────────────────────────────────── // // A button rather than confirming on load. A mail client or scanner that // pre-fetches links would otherwise spend the token before the person ever saw // it, and this token is single-use. return (

Confirm that{' '} {link.email ? {link.email} : 'this address'} should be the contact and account-recovery address for {link.username ? ( <> {' '} {link.username} ) : ( ' this account' )} .

{error && (

{error}

)}

If you did not ask for this, close this page. Nothing changes and no account of yours is affected.

) }