91 lines
2.6 KiB
TypeScript
91 lines
2.6 KiB
TypeScript
/**
|
|
* Renders the inline editor of the signed-in user's email, in their profile's
|
|
* header.
|
|
*
|
|
* @module
|
|
*/
|
|
|
|
import { t } from "@lingui/core/macro";
|
|
import { Trans } from "@lingui/react/macro";
|
|
import { FormProvider } from "react-hook-form";
|
|
import { API_URL } from "../../config/api.ts";
|
|
import type { Wire } from "../../../shared/api.ts";
|
|
import type { PublicUser, UpdateUserRequest } from "../../../shared/user.ts";
|
|
import { expectOk } from "../../utils/shared/api.ts";
|
|
import { useAuth } from "../../hooks/auth/useAuth.ts";
|
|
import { useApiForm } from "../../hooks/form/useApiForm.ts";
|
|
import { FormError } from "../form/FormError.tsx";
|
|
import { SubmitButton } from "../form/SubmitButton.tsx";
|
|
|
|
/**
|
|
* Passed to {@link EmailEditor} by `ProfileHeader`, which shows it in place of
|
|
* the email while editing.
|
|
*/
|
|
export interface EmailEditorProps {
|
|
initialEmail: string;
|
|
onCancel: () => void;
|
|
onSaved: (email: string) => void;
|
|
}
|
|
|
|
/**
|
|
* Renders the email field, saved to `PATCH /api/me`. Escape cancels.
|
|
*/
|
|
export function EmailEditor(
|
|
{ initialEmail, onCancel, onSaved }: EmailEditorProps,
|
|
): React.JSX.Element {
|
|
const { authFetch } = useAuth();
|
|
const form = useApiForm<{ email: string }>({
|
|
defaultValues: { email: initialEmail },
|
|
});
|
|
const email = form.watch("email");
|
|
const submitting = form.formState.isSubmitting;
|
|
|
|
const onSubmit = form.submit(async ({ email }) => {
|
|
await expectOk<Wire<PublicUser>>(
|
|
await authFetch(`${API_URL}/api/me`, {
|
|
method: "PATCH",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify(
|
|
{ email: email.trim() } satisfies UpdateUserRequest,
|
|
),
|
|
}),
|
|
);
|
|
onSaved(email.trim());
|
|
});
|
|
|
|
return (
|
|
<FormProvider {...form}>
|
|
<form className="profile-email-editor animate-in" onSubmit={onSubmit}>
|
|
<input
|
|
type="email"
|
|
className="profile-email-input"
|
|
{...form.register("email")}
|
|
onKeyDown={(e) => {
|
|
if (e.key === "Escape") onCancel();
|
|
}}
|
|
disabled={submitting}
|
|
autoFocus
|
|
/>
|
|
<div className="profile-email-actions">
|
|
<SubmitButton
|
|
className="btn-primary btn-sm"
|
|
pendingLabel={<Trans>Saving…</Trans>}
|
|
disabled={!email.trim()}
|
|
>
|
|
<Trans>Save</Trans>
|
|
</SubmitButton>
|
|
<button
|
|
type="button"
|
|
className="btn-secondary btn-sm"
|
|
onClick={onCancel}
|
|
disabled={submitting}
|
|
>
|
|
<Trans>Cancel</Trans>
|
|
</button>
|
|
</div>
|
|
<FormError title={t`Failed to save`} />
|
|
</form>
|
|
</FormProvider>
|
|
);
|
|
}
|