import { useState } from 'react' import { api } from '../../api/client.js' // Self-service email address (engagement Phase 1b). Shared by the player portal // and the admin account screen, the same way TrustedDevicesPanel and // RecoveryCodesPanel are — /auth/me/account is one surface for every role, so its // UI is one component too. // // The property this component exists to make visible: a requested address is // STAGED, not applied. The account keeps receiving mail — password resets // included — at the address it already has until the emailed link is opened. If // the UI let a pending address look like the address in force, someone who // mistyped would believe the change took and would only discover otherwise when // they could not recover their account. // // `hasPassword` decides whether the current-password field appears: an address is // where account recovery lands, so changing it is re-authenticated, with the same // carve-out the password form makes for an SSO-only account. export default function EmailAddressPanel({ account, reload, embedded = false }) { const hasPassword = account.has_password !== false const [email, setEmail] = useState('') const [current, setCurrent] = useState('') const [busy, setBusy] = useState(false) const [msg, setMsg] = useState('') const [error, setError] = useState('') const pending = account.email_pending async function save(e) { e.preventDefault() setMsg('') setError('') setBusy(true) try { const res = await api.changeEmail(email.trim(), hasPassword ? current : undefined) setEmail('') setCurrent('') // Report an unsent mail honestly. Saying "check your inbox" about a message // that was never sent turns a configuration problem into a user who waits. if (res.emailed === false) { setMsg( res.reason === 'NOT_CONFIGURED' ? 'Address saved, but this site cannot send email right now. Ask an administrator, then use Resend.' : 'Address saved, but the confirmation email could not be sent. Try Resend in a moment.', ) } else { setMsg( `Confirmation sent to ${res.email_pending}. Your current address stays in use until you open that link.`, ) } await reload() } catch (err) { if (err.status === 429) setError('Too many confirmation emails. Try again later.') else setError(err.message || 'Could not change your email address.') } finally { setBusy(false) } } async function resend() { setMsg('') setError('') setBusy(true) try { const res = await api.resendEmailVerification() setMsg( res.emailed === false ? 'Could not send the confirmation email.' : `Confirmation re-sent to ${res.email_pending}.`, ) } catch (err) { setError(err.message || 'Could not resend the confirmation email.') } finally { setBusy(false) } } async function discard() { setMsg('') setError('') setBusy(true) try { await api.cancelEmailChange() setMsg('Pending address discarded.') await reload() } catch (err) { setError(err.message || 'Could not discard the pending address.') } finally { setBusy(false) } } const wrap = embedded ? {} : { marginTop: 40, borderTop: '1px solid var(--line-soft)', paddingTop: 28 } return (
{account.email ? ( <> Currently {account.email} {account.email_verified ? ' (confirmed)' : ' (not yet confirmed)'}. This is where password-reset email is sent. > ) : ( 'You have no email address on file, so you cannot reset your password by email.' )}
{pending && (