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   12x                                 5x 12x 12x 12x           12x 12x 2x               12x   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>
  );
}