diff --git a/package.json b/package.json index 11cad540..7c1b8386 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "i18next": "^21.8.0", "i18next-browser-languagedetector": "^6.1.4", "i18next-http-backend": "^1.4.0", - "js-api-client": "https://github.com/opexdev/js-api-client.git#v1.0.1-beta.2", + "js-api-client": "https://github.com/opexdev/js-api-client.git#develop", "jwt-decode": "^3.1.2", "lightweight-charts": "^3.8.0", "moment-jalaali": "^0.9.2", diff --git a/public/assets/locales/en/translation.json b/public/assets/locales/en/translation.json index ae45da0f..20f70e1c 100644 --- a/public/assets/locales/en/translation.json +++ b/public/assets/locales/en/translation.json @@ -474,6 +474,12 @@ "wrongOTP": "Entered OTP is incorrect!", "wrongPasswordConfirmation": "Password and confirm password don't match.", "forgetPassword": "Forget Password", + "verifyEmail": "Didn't get email ?", + "resendVerifyEmail": "Resend verification email", + "sendEmail": "Send email", + "verifyEmailServerError": "Server error to send verify email, try again!", + "verifyEmailFinished": "Verify email sent successfully!", + "userAlreadyVerified": "User already verified", "accountNotActive": "Your account has not been activated yet.", "enter": "Enter", "resetPassword": "Reset Password", diff --git a/public/assets/locales/fa/translation.json b/public/assets/locales/fa/translation.json index f9dfdb5a..a21b2bb0 100644 --- a/public/assets/locales/fa/translation.json +++ b/public/assets/locales/fa/translation.json @@ -474,6 +474,12 @@ "wrongOTP": "رمز دو مرحله ای صحیح نمی باشد!", "wrongPasswordConfirmation": "رمز ورود و تکرار آن باهم مطابقت ندارند.!", "forgetPassword": "فراموشی رمز عبور", + "verifyEmail": "ایمیل دریافت نکرده اید؟", + "resendVerifyEmail": "ارسال دوباره ایمیل فعال‌سازی", + "sendEmail": "ارسال ایمیل", + "verifyEmailServerError": "خطا در ارسال ایمیل فعال‌سازی، دوباره تلاش کنید", + "verifyEmailFinished": "ایمیل فعال‌سازی با موفقیت ارسال شد.", + "userAlreadyVerified": "ایمیل شما فعال شده است.", "accountNotActive": "اکانت شما هنوز فعال نشده است", "enter": "ورود", "resetPassword": "بازیابی رمز", diff --git a/src/main/Browser/Pages/Login/Login.module.css b/src/main/Browser/Pages/Login/Login.module.css index 93ddb195..21188ea8 100644 --- a/src/main/Browser/Pages/Login/Login.module.css +++ b/src/main/Browser/Pages/Login/Login.module.css @@ -156,6 +156,21 @@ direction: ltr !important; } + +.disable:disabled,.button[disabled] { + border: 0.3vh solid var(--cardHeader); + background: var(--cardHeader); + color: var(--textColor); + cursor: not-allowed; +} + +.thisButton:disabled,.button[disabled] { + border: 0.3vh solid var(--cardHeader); + background: var(--cardHeader); + color: var(--textColor); + cursor: not-allowed; +} + .twinkle{ -webkit-animation: twinkle 4s linear infinite; -moz-animation: twinkle 4s linear infinite; diff --git a/src/main/Browser/Pages/Login/components/EmailVerification/EmailVerification.js b/src/main/Browser/Pages/Login/components/EmailVerification/EmailVerification.js new file mode 100644 index 00000000..3231e116 --- /dev/null +++ b/src/main/Browser/Pages/Login/components/EmailVerification/EmailVerification.js @@ -0,0 +1,214 @@ +import React, {useEffect, useState} from 'react'; +import classes from "../../Login.module.css"; +import Button from "../../../../../../components/Button/Button"; +import {useTranslation} from "react-i18next"; +import {getCaptchaImage, getPanelToken, requestForVerifyEmail} from "js-api-client"; +import ReactTooltip from "react-tooltip"; +import LoginFormLoading from "../LoginLoading/LoginFormLoading"; +import {validateEmail} from "../../../../../../utils/utils"; +import {images} from "../../../../../../assets/images"; +import TextInput from "../../../../../../components/TextInput/TextInput"; +import Icon from "../../../../../../components/Icon/Icon"; +import Countdown from "react-countdown"; +import {useSelector} from "react-redux"; + +const EmailVerification = ({returnFunc, email, disable, returnFuncDisable}) => { + + const clientSecret = window.env.REACT_APP_CLIENT_SECRET + const clientId = window.env.REACT_APP_CLIENT_ID + + const {t} = useTranslation(); + + const [loading, setLoading] = useState(false); + const [isLoading, setIsLoading] = useState(true); + const [success, setSuccess] = useState(false); + + const verifyEmailLock = useSelector((state) => state.exchange.verifyEmailLock) + + const [activeEmail, setActiveEmail] = useState({ + email: {value: "", error: []}, + captchaAnswer: {value: "", error: []}, + }); + const [captcha, setCaptcha] = useState({ + image: {value: "", error: []}, + SessionKey: {value: "", error: []}, + expireTime: {value: "", error: []}, + }); + + const captchaReq = () => { + setIsLoading(true) + getCaptchaImage() + .then((res) => { + setCaptcha({ + image: { + value: `data:${res.headers['content-type']};base64,${Buffer.from(res.data).toString('base64')}`, + error: [] + }, + SessionKey: {value: res.headers['captcha-session-key'], error: []}, + expireTime: {value: res.headers['captcha-expire-timestamp'], error: []}, + }) + }).catch(() => { + setActiveEmail({...activeEmail, captchaAnswer: {value: "", error: [t("login.captchaServerError")]}}) + setCaptcha({...captcha, image: {value: undefined, error: []}}) + }).finally(() => { + setIsLoading(false) + }); + } + + useEffect(() => { + captchaReq() + if (email.length > 0) return setActiveEmail({...activeEmail, email: {...activeEmail.email, value: email}}) + }, []) + + + useEffect(() => { + ReactTooltip.rebuild(); + }); + + if (loading) return + + const submit = async (e) => { + e.preventDefault(); + if (activeEmail.email.value === "") { + setActiveEmail({...activeEmail, email: {value: "", error: [t('login.emptyEmail')]}}) + return; + } + if (!validateEmail(activeEmail.email.value)) { + setActiveEmail({...activeEmail, email: {value: "", error: [t('login.forgetPassEmailForgetError')]}}) + return; + } + if (activeEmail.captchaAnswer.value === "") { + setActiveEmail({...activeEmail, captchaAnswer: {value: "", error: [t('login.emptyCaptcha')]}}) + return; + } + if (activeEmail.captchaAnswer.value.length < 5) { + setActiveEmail({...activeEmail, captchaAnswer: {value: "", error: [t('login.minCaptcha')]}}) + return; + } + setLoading(true); + + const {data: {access_token: panelToken}} = await getPanelToken(clientId, clientSecret); + const captchaValue = `${captcha.SessionKey.value}-${activeEmail.captchaAnswer.value}` + requestForVerifyEmail(activeEmail.email.value, captchaValue) + .then(() => { + setSuccess(true) + }) + .catch((err) => { + if (err?.response?.data?.code === 10001 && err?.response?.data?.message === "Captcha is not valid") { + console.log("in captcha erro") + return setActiveEmail({...activeEmail, captchaAnswer: {value: "", error: [t("login.InvalidCaptcha")]}}) + } + if (err?.response?.data?.code === 1002 && err?.response?.data?.message === "User already verified") { + return setActiveEmail({...activeEmail, captchaAnswer: {value: "", error: [t("login.userAlreadyVerified")]}}) + } else { + return setActiveEmail({ + ...activeEmail, + captchaAnswer: {value: "", error: [t('login.verifyEmailServerError')]} + }) + } + }) + .finally(() => { + setLoading(false); + }); + + } + + const LeadCaptchaHandler = () => { + if (isLoading) return linearLoading + + if (captcha.image.value === undefined) return {t('captchaAnswer')} + + return + } + + const sendEmailButtonTitle = () => { + if (disable) { + return {t('login.sendEmail')} (
{props.minutes}:{props.seconds}
} + onComplete={returnFuncDisable} + />)
+ } + return {t('login.sendEmail')} + } + + + const FormBody = () => { + if (success) return {t('login.verifyEmailFinished')} + + return <> + {t('login.resendVerifyEmail')} + + setActiveEmail({...activeEmail, email: {value: e.target.value, error: []}}) + } + alerts={activeEmail.email.error} + /> + ${t("login.refreshCaptcha")}
`}>} + type="text" + data-name="captchaAnswer" + data-type="input" + data-min={5} + customClass={`${classes.loginInput} ${classes.captcha}`} + value={activeEmail.captchaAnswer.value} + onchange={(e) => + setActiveEmail({...activeEmail, captchaAnswer: {value: e.target.value, error: []}}) + } + alerts={activeEmail.captchaAnswer.error} + maxLength="5" + /> + + } + + + const FormFooter = () => { + if (success) return