From 4eaefd1d9b422e94ee6d41078865a1af0477bf91 Mon Sep 17 00:00:00 2001 From: SleepWalker Date: Sun, 22 May 2016 17:01:31 +0300 Subject: [PATCH] #26: email resend count down for forgot-password --- src/components/auth/BaseAuthBody.jsx | 7 ++++-- src/components/auth/PanelTransition.jsx | 10 ++++++-- src/components/auth/actions.js | 9 ++++++- src/components/auth/authError/AuthError.jsx | 28 +++++++++++++++++++-- src/i18n/en.json | 2 +- src/i18n/ru.json | 2 +- src/services/errorsDict.intl.json | 2 +- src/services/errorsDict.js | 14 ++++++++--- 8 files changed, 61 insertions(+), 13 deletions(-) diff --git a/src/components/auth/BaseAuthBody.jsx b/src/components/auth/BaseAuthBody.jsx index f2784ef..2c12589 100644 --- a/src/components/auth/BaseAuthBody.jsx +++ b/src/components/auth/BaseAuthBody.jsx @@ -13,7 +13,10 @@ export default class BaseAuthBody extends Component { resolve: PropTypes.func.isRequired, requestRedraw: PropTypes.func.isRequired, auth: PropTypes.shape({ - error: PropTypes.string, + error: PropTypes.oneOfType([PropTypes.string, PropTypes.shape({ + type: PropTypes.string, + payload: PropTypes.object + })]), scopes: PropTypes.array }), user: userShape @@ -22,7 +25,7 @@ export default class BaseAuthBody extends Component { renderErrors() { return this.context.auth.error ? - : '' + : null ; } diff --git a/src/components/auth/PanelTransition.jsx b/src/components/auth/PanelTransition.jsx index 484c50a..661fbba 100644 --- a/src/components/auth/PanelTransition.jsx +++ b/src/components/auth/PanelTransition.jsx @@ -59,7 +59,10 @@ class PanelTransition extends Component { static propTypes = { // context props auth: PropTypes.shape({ - error: PropTypes.string, + error: PropTypes.oneOfType([PropTypes.string, PropTypes.shape({ + type: PropTypes.string, + payload: PropTypes.object + })]), isLoading: PropTypes.bool, login: PropTypes.shape({ login: PropTypes.string, @@ -82,7 +85,10 @@ class PanelTransition extends Component { static childContextTypes = { auth: PropTypes.shape({ - error: PropTypes.string, + error: PropTypes.oneOfType([PropTypes.string, PropTypes.shape({ + type: PropTypes.string, + payload: PropTypes.object + })]), login: PropTypes.shape({ login: PropTypes.string, password: PropTypes.string diff --git a/src/components/auth/actions.js b/src/components/auth/actions.js index 708fa3b..1b0a090 100644 --- a/src/components/auth/actions.js +++ b/src/components/auth/actions.js @@ -335,7 +335,14 @@ function needActivation() { function validationErrorsHandler(dispatch) { return (resp) => { if (resp.errors) { - const errorMessage = resp.errors[Object.keys(resp.errors)[0]]; + let errorMessage = resp.errors[Object.keys(resp.errors)[0]]; + if (resp.data) { + errorMessage = { + type: errorMessage, + payload: resp.data + }; + } + dispatch(setError(errorMessage)); return Promise.reject(errorMessage); } diff --git a/src/components/auth/authError/AuthError.jsx b/src/components/auth/authError/AuthError.jsx index 10e09b7..cf51c94 100644 --- a/src/components/auth/authError/AuthError.jsx +++ b/src/components/auth/authError/AuthError.jsx @@ -3,9 +3,30 @@ import React, { PropTypes } from 'react'; import errorsDict from 'services/errorsDict'; import { PanelBodyHeader } from 'components/ui/Panel'; +let autoHideTimer; +function resetTimer() { + if (autoHideTimer) { + clearTimeout(autoHideTimer); + autoHideTimer = null; + } +} export default function AuthError({error, onClose = function() {}}) { + resetTimer(); + + if (error.type === 'error.email_frequency') { + if (error.payload && error.payload.canRepeatIn) { + error.payload.msLeft = error.payload.canRepeatIn * 1000; + setTimeout(onClose, error.payload.msLeft - Date.now() + 1500); // 1500 to let the user see, that time is elapsed + } else { + // TODO: it would be greate to log this case, when we will setup frontend logging + } + } + return ( - + { + resetTimer(); + onClose(); + }}> {errorsDict.resolve(error)} ); @@ -13,6 +34,9 @@ export default function AuthError({error, onClose = function() {}}) { AuthError.displayName = 'AuthError'; AuthError.propTypes = { - error: PropTypes.string.isRequired, + error: PropTypes.oneOfType([PropTypes.string, PropTypes.shape({ + type: PropTypes.string, + payload: PropTypes.object + })]).isRequired, onClose: PropTypes.func }; diff --git a/src/i18n/en.json b/src/i18n/en.json index 829de8b..ea64d51 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -100,7 +100,7 @@ "components.userbar.register": "Join", "pages.root.siteName": "Ely.by", "services.accountNotActivated": "The account is not activated", - "services.emailFrequency": "Please cool down, you are requesting emails too often. New key can be retrieved after 30 minutes from the previous request.", + "services.emailFrequency": "Please cool down, you are requesting emails too often. New key can be retrieved {time}.", "services.emailInvalid": "Email is invalid", "services.emailIsTempmail": "Tempmail E-mail addresses is not allowed", "services.emailNotAvailable": "This email is already registered.", diff --git a/src/i18n/ru.json b/src/i18n/ru.json index c45e63c..e8db73a 100644 --- a/src/i18n/ru.json +++ b/src/i18n/ru.json @@ -100,7 +100,7 @@ "components.userbar.register": "Регистрация", "pages.root.siteName": "Ely.by", "services.accountNotActivated": "Аккаунт не активирован", - "services.emailFrequency": "Пожалуйста, успокойтесь, вы запрашиваете E-mail слишком часто. Новый ключ можно будет заказать через 30 минут от предыдущего запроса.", + "services.emailFrequency": "Пожалуйста, успокойтесь, вы запрашиваете E-mail слишком часто. Новый ключ можно будет заказать {time}.", "services.emailInvalid": "Указан неправильный E-mail", "services.emailIsTempmail": "Использование сервисов временных E-mail адресов запрещено", "services.emailNotAvailable": "На указанный E-mail адрес уже зарегистрирован аккаунт.", diff --git a/src/services/errorsDict.intl.json b/src/services/errorsDict.intl.json index fd0750d..33641d7 100644 --- a/src/services/errorsDict.intl.json +++ b/src/services/errorsDict.intl.json @@ -23,7 +23,7 @@ "rulesAgreementRequired": "You must accept rules in order to create an account", "keyRequired": "Please, enter an activation key", "keyNotExists": "The key is incorrect", - "emailFrequency": "Please cool down, you are requesting emails too often. New key can be retrieved after 30 minutes from the previous request.", + "emailFrequency": "Please cool down, you are requesting emails too often. New key can be retrieved {time}.", "accountNotActivated": "The account is not activated", "oldHashStrategy": "Sorry, but your account's password is too old. Please change your password in order to perform this action." } diff --git a/src/services/errorsDict.js b/src/services/errorsDict.js index a0f7438..158d932 100644 --- a/src/services/errorsDict.js +++ b/src/services/errorsDict.js @@ -1,12 +1,18 @@ import React from 'react'; -import { FormattedMessage as Message } from 'react-intl'; +import { FormattedMessage as Message, FormattedRelative as Relative } from 'react-intl'; import messages from './errorsDict.intl.json'; export default { resolve(error) { - return errorsMap[error] ? errorsMap[error]() : error; + let payload = {}; + + if (error.type) { + payload = error.payload; + error = error.type; + } + return errorsMap[error] ? errorsMap[error](payload) : error; } }; @@ -70,5 +76,7 @@ const errorsMap = { 'error.account_not_activated': () => , - 'error.email_frequency': () => + 'error.email_frequency': (props) => + }} /> };