mirror of
https://github.com/djdevin/recflare.git
synced 2026-10-25 23:18:51 -07:00
[www] add displayname change
This commit is contained in:
@@ -506,6 +506,24 @@ async function changeUsername(username: string): Promise<SelfAccount> {
|
|||||||
return fetchMe()
|
return fetchMe()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the display name — the name other players read, as opposed to the username the
|
||||||
|
* account signs in with. Unrationed, unlike a username change.
|
||||||
|
*
|
||||||
|
* Not checked here first, for the reason the email below isn't: `accounts` owns the rule
|
||||||
|
* (`displayNameRejection` in `@repo/domain`, the game client's own charset), and that
|
||||||
|
* module can't come into the browser — it imports `isemail`. The route refuses with an
|
||||||
|
* empty 400, so the rule is spelled out in the refusal instead.
|
||||||
|
*/
|
||||||
|
const saveDisplayName = (displayName: string): Promise<unknown> =>
|
||||||
|
call(`${where().accounts}/account/me/displayname`, {
|
||||||
|
method: 'PUT',
|
||||||
|
form: { displayName },
|
||||||
|
authed: true,
|
||||||
|
refusal:
|
||||||
|
"That display name can't be used. Use up to 15 letters, numbers, spaces and .,'!?_&()#@:+- — and no profanity.",
|
||||||
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the account's email.
|
* Set the account's email.
|
||||||
*
|
*
|
||||||
@@ -3810,6 +3828,11 @@ function Dashboard({
|
|||||||
label: 'Username',
|
label: 'Username',
|
||||||
render: () => <UsernameForm account={account} onChange={onChange} />,
|
render: () => <UsernameForm account={account} onChange={onChange} />,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'displayname',
|
||||||
|
label: 'Display name',
|
||||||
|
render: () => <DisplayNameForm account={account} onChange={onChange} />,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'email',
|
id: 'email',
|
||||||
label: 'Email',
|
label: 'Email',
|
||||||
@@ -4609,6 +4632,61 @@ function UsernameForm({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function DisplayNameForm({
|
||||||
|
account,
|
||||||
|
onChange,
|
||||||
|
}: {
|
||||||
|
account: SelfAccount
|
||||||
|
onChange: (a: SelfAccount) => void
|
||||||
|
}) {
|
||||||
|
const [displayName, setDisplayName] = useState(account.displayName)
|
||||||
|
const { pending, error, done, run } = useAction()
|
||||||
|
|
||||||
|
const unchanged = displayName.trim() === account.displayName
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="card">
|
||||||
|
<h2>Display name</h2>
|
||||||
|
<p className="muted">
|
||||||
|
The name other players see in the game. Changing it doesn't change what you sign in
|
||||||
|
with.
|
||||||
|
</p>
|
||||||
|
<form
|
||||||
|
onSubmit={(e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
void run(async () => {
|
||||||
|
const saved = displayName.trim()
|
||||||
|
await saveDisplayName(saved)
|
||||||
|
onChange({ ...account, displayName: saved })
|
||||||
|
setDisplayName(saved)
|
||||||
|
return 'Display name saved.'
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<label>
|
||||||
|
Display name
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={displayName}
|
||||||
|
autoComplete="nickname"
|
||||||
|
maxLength={15}
|
||||||
|
onChange={(e) => setDisplayName(e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<span className="hint">
|
||||||
|
Up to 15 characters: letters, numbers, spaces and .,'!?_&()#@:+-
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
{error && <p className="error">{error}</p>}
|
||||||
|
{done && <p className="ok">{done}</p>}
|
||||||
|
<button type="submit" disabled={pending || unchanged}>
|
||||||
|
{pending ? 'Saving…' : 'Save display name'}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function EmailForm({
|
function EmailForm({
|
||||||
account,
|
account,
|
||||||
onChange,
|
onChange,
|
||||||
|
|||||||
Reference in New Issue
Block a user