SetPasswordForm.tsx 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. import { isWeakPassword } from "@ente/accounts/utils";
  2. import ShowHidePassword from "@ente/shared/components/Form/ShowHidePassword";
  3. import SubmitButton from "@ente/shared/components/SubmitButton";
  4. import { Box, Input, TextField, Typography } from "@mui/material";
  5. import { Formik } from "formik";
  6. import { t } from "i18next";
  7. import React, { useState } from "react";
  8. import { Trans } from "react-i18next";
  9. import * as Yup from "yup";
  10. import { PasswordStrengthHint } from "./PasswordStrength";
  11. export interface SetPasswordFormProps {
  12. userEmail: string;
  13. callback: (
  14. passphrase: string,
  15. setFieldError: (
  16. field: keyof SetPasswordFormValues,
  17. message: string,
  18. ) => void,
  19. ) => Promise<void>;
  20. buttonText: string;
  21. }
  22. export interface SetPasswordFormValues {
  23. passphrase: string;
  24. confirm: string;
  25. }
  26. function SetPasswordForm(props: SetPasswordFormProps) {
  27. const [loading, setLoading] = useState(false);
  28. const [showPassword, setShowPassword] = useState(false);
  29. const handleClickShowPassword = () => {
  30. setShowPassword(!showPassword);
  31. };
  32. const handleMouseDownPassword = (
  33. event: React.MouseEvent<HTMLButtonElement>,
  34. ) => {
  35. event.preventDefault();
  36. };
  37. const onSubmit = async (
  38. values: SetPasswordFormValues,
  39. {
  40. setFieldError,
  41. }: {
  42. setFieldError: (
  43. field: keyof SetPasswordFormValues,
  44. message: string,
  45. ) => void;
  46. },
  47. ) => {
  48. setLoading(true);
  49. try {
  50. const { passphrase, confirm } = values;
  51. if (passphrase === confirm) {
  52. await props.callback(passphrase, setFieldError);
  53. } else {
  54. setFieldError("confirm", t("PASSPHRASE_MATCH_ERROR"));
  55. }
  56. } catch (e) {
  57. const message = e instanceof Error ? e.message : "";
  58. setFieldError("confirm", `${t("UNKNOWN_ERROR")} ${message}`);
  59. } finally {
  60. setLoading(false);
  61. }
  62. };
  63. return (
  64. <Formik<SetPasswordFormValues>
  65. initialValues={{ passphrase: "", confirm: "" }}
  66. validationSchema={Yup.object().shape({
  67. passphrase: Yup.string().required(t("REQUIRED")),
  68. confirm: Yup.string().required(t("REQUIRED")),
  69. })}
  70. validateOnChange={false}
  71. validateOnBlur={false}
  72. onSubmit={onSubmit}
  73. >
  74. {({ values, errors, handleChange, handleSubmit }) => (
  75. <form noValidate onSubmit={handleSubmit}>
  76. <Typography mb={2} color="text.muted" variant="small">
  77. {t("ENTER_ENC_PASSPHRASE")}
  78. </Typography>
  79. <Input
  80. sx={{ display: "none" }}
  81. name="email"
  82. id="email"
  83. autoComplete="username"
  84. type="email"
  85. value={props.userEmail}
  86. />
  87. <TextField
  88. fullWidth
  89. name="password"
  90. id="password"
  91. autoComplete="new-password"
  92. type={showPassword ? "text" : "password"}
  93. label={t("PASSPHRASE_HINT")}
  94. value={values.passphrase}
  95. onChange={handleChange("passphrase")}
  96. error={Boolean(errors.passphrase)}
  97. helperText={errors.passphrase}
  98. autoFocus
  99. disabled={loading}
  100. InputProps={{
  101. endAdornment: (
  102. <ShowHidePassword
  103. showPassword={showPassword}
  104. handleClickShowPassword={
  105. handleClickShowPassword
  106. }
  107. handleMouseDownPassword={
  108. handleMouseDownPassword
  109. }
  110. />
  111. ),
  112. }}
  113. />
  114. <TextField
  115. fullWidth
  116. name="confirm-password"
  117. id="confirm-password"
  118. autoComplete="new-password"
  119. type="password"
  120. label={t("CONFIRM_PASSPHRASE")}
  121. value={values.confirm}
  122. onChange={handleChange("confirm")}
  123. disabled={loading}
  124. error={Boolean(errors.confirm)}
  125. helperText={errors.confirm}
  126. />
  127. <PasswordStrengthHint password={values.passphrase} />
  128. <Typography my={2} variant="small">
  129. <Trans i18nKey={"PASSPHRASE_DISCLAIMER"} />
  130. </Typography>
  131. <Box my={4}>
  132. <SubmitButton
  133. sx={{ my: 0 }}
  134. loading={loading}
  135. buttonText={props.buttonText}
  136. disabled={isWeakPassword(values.passphrase)}
  137. />
  138. {loading && (
  139. <Typography
  140. textAlign="center"
  141. mt={1}
  142. color="text.muted"
  143. variant="small"
  144. >
  145. {t("KEY_GENERATION_IN_PROGRESS_MESSAGE")}
  146. </Typography>
  147. )}
  148. </Box>
  149. </form>
  150. )}
  151. </Formik>
  152. );
  153. }
  154. export default SetPasswordForm;