All files / app/features/auth/email ChangeEmail.tsx

100% Statements 23/23
100% Branches 2/2
100% Functions 5/5
100% Lines 22/22

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 1081x 1x 1x 1x 1x     1x 1x 1x 1x 1x 1x   13x                                 5x 13x 13x 13x           13x 13x 2x               13x   2x   1x                                                                                                          
import { styled, Typography, useMediaQuery, useTheme } from "@mui/material";
import { useMutation } from "@tanstack/react-query";
import Alert from "components/Alert";
import Button from "components/Button";
import TextField from "components/TextField";
import { Empty } from "google-protobuf/google/protobuf/empty_pb";
import { RpcError } from "grpc-web";
import { Trans, useTranslation } from "i18n";
import { AUTH, GLOBAL } from "i18n/namespaces";
import { useForm } from "react-hook-form";
import { service } from "service";
import { theme } from "theme";
import { lowercaseAndTrimField } from "utils/validation";
 
const StyledForm = styled("form")(() => ({
  marginBottom: theme.spacing(2),
  "& > * + *": {
    marginBlockStart: theme.spacing(1),
  },
}));
 
interface ChangeEmailFormData {
  newEmail: string;
  currentPassword: string;
}
 
interface ChangeEmailProps {
  email: string;
  className?: string;
}
 
export default function ChangeEmail({ className, email }: ChangeEmailProps) {
  const { t } = useTranslation([AUTH, GLOBAL]);
  const theme = useTheme();
  const isMdOrWider = useMediaQuery(theme.breakpoints.up("md"));
 
  const {
    handleSubmit,
    register,
    reset: resetForm,
  } = useForm<ChangeEmailFormData>();
  const onSubmit = handleSubmit(({ currentPassword, newEmail }) => {
    changeEmail({ currentPassword, newEmail: lowercaseAndTrimField(newEmail) });
  });
 
  const {
    error: changeEmailError,
    isPending: isChangeEmailLoading,
    isSuccess: isChangeEmailSuccess,
    mutate: changeEmail,
  } = useMutation<Empty, RpcError, ChangeEmailFormData>({
    mutationFn: ({ currentPassword, newEmail }) =>
      service.account.changeEmail(newEmail, currentPassword),
    onSuccess: () => {
      resetForm();
    },
  });
 
  return (
    <div className={className}>
      <Typography variant="h2">{t("auth:change_email_form.title")}</Typography>
      <>
        <Typography variant="body1">
          <Trans
            i18nKey="auth:change_email_form.current_email_message"
            values={{ email }}
          >
            {`Your email address is currently `}
            <strong>{email}</strong>
            {`.`}
          </Trans>
        </Typography>
        {changeEmailError && (
          <Alert severity="error">{changeEmailError.message}</Alert>
        )}
        {isChangeEmailSuccess && (
          <Alert severity="success">
            {t("auth:change_email_form.success_message")}
          </Alert>
        )}
        <StyledForm onSubmit={onSubmit}>
          <TextField
            id="currentPassword"
            {...register("currentPassword", { required: true })}
            label={t("auth:change_email_form.current_password")}
            type="password"
            fullWidth={!isMdOrWider}
          />
          <TextField
            id="newEmail"
            {...register("newEmail", { required: true })}
            label={t("auth:change_email_form.new_email")}
            name="newEmail"
            fullWidth={!isMdOrWider}
          />
          <Button
            fullWidth={!isMdOrWider}
            loading={isChangeEmailLoading}
            type="submit"
          >
            {t("global:submit")}
          </Button>
        </StyledForm>
      </>
    </div>
  );
}