mirror of
https://github.com/elyby/accounts-frontend.git
synced 2024-11-17 21:53:03 +05:30
#14: add captcha support for resend-activation
This commit is contained in:
parent
4f7f1d2273
commit
4716c679b9
@ -103,9 +103,9 @@ export function activate({key = ''}) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resendActivation({email = ''}) {
|
export function resendActivation({email = '', captcha}) {
|
||||||
return wrapInLoader((dispatch) =>
|
return wrapInLoader((dispatch) =>
|
||||||
signup.resendActivation({email})
|
signup.resendActivation({email, captcha})
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
dispatch(updateUser({
|
dispatch(updateUser({
|
||||||
email
|
email
|
||||||
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||||||
|
|
||||||
import { FormattedMessage as Message } from 'react-intl';
|
import { FormattedMessage as Message } from 'react-intl';
|
||||||
|
|
||||||
import { Input } from 'components/ui/form';
|
import { Input, Captcha } from 'components/ui/form';
|
||||||
import registerMessages from 'components/auth/register/Register.intl.json';
|
import registerMessages from 'components/auth/register/Register.intl.json';
|
||||||
|
|
||||||
import BaseAuthBody from 'components/auth/BaseAuthBody';
|
import BaseAuthBody from 'components/auth/BaseAuthBody';
|
||||||
@ -25,7 +25,6 @@ export default class ResendActivation extends BaseAuthBody {
|
|||||||
<Message {...messages.specifyYourEmail} />
|
<Message {...messages.specifyYourEmail} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.formRow}>
|
|
||||||
<Input {...this.bindField('email')}
|
<Input {...this.bindField('email')}
|
||||||
icon="envelope"
|
icon="envelope"
|
||||||
color="blue"
|
color="blue"
|
||||||
@ -34,7 +33,8 @@ export default class ResendActivation extends BaseAuthBody {
|
|||||||
placeholder={registerMessages.yourEmail}
|
placeholder={registerMessages.yourEmail}
|
||||||
defaultValue={this.context.user.email}
|
defaultValue={this.context.user.email}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
|
<Captcha {...this.bindField('captcha')} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
38
src/components/ui/form/Captcha.jsx
Normal file
38
src/components/ui/form/Captcha.jsx
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import React, { PropTypes } from 'react';
|
||||||
|
|
||||||
|
import captcha from 'services/captcha';
|
||||||
|
import { skins, SKIN_DARK } from 'components/ui';
|
||||||
|
|
||||||
|
import styles from './form.scss';
|
||||||
|
import FormInputComponent from './FormInputComponent';
|
||||||
|
|
||||||
|
export default class Captcha extends FormInputComponent {
|
||||||
|
static displayName = 'Captcha';
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
skin: PropTypes.oneOf(skins)
|
||||||
|
};
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
skin: SKIN_DARK
|
||||||
|
};
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
captcha.render(this.el, {
|
||||||
|
skin: this.props.skin,
|
||||||
|
onSetCode: this.setCode
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div ref={this.setEl} className={styles.captcha} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getValue() {
|
||||||
|
return this.state && this.state.code;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCode = (code) => this.setState({code});
|
||||||
|
}
|
@ -295,6 +295,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.captcha {
|
||||||
|
width: 302px;
|
||||||
|
height: 76px;
|
||||||
|
// minimum captcha width is 302px, which can not be changed
|
||||||
|
// using transform to scale down to 296px
|
||||||
|
transform-origin: 0;
|
||||||
|
transform: scaleX(0.98);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Form validation
|
* Form validation
|
||||||
*/
|
*/
|
||||||
|
@ -5,6 +5,7 @@ import Button from './Button';
|
|||||||
import Form from './Form';
|
import Form from './Form';
|
||||||
import FormModel from './FormModel';
|
import FormModel from './FormModel';
|
||||||
import Dropdown from './Dropdown';
|
import Dropdown from './Dropdown';
|
||||||
|
import Captcha from './Captcha';
|
||||||
import FormError from './FormError';
|
import FormError from './FormError';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
@ -15,5 +16,6 @@ export {
|
|||||||
Form,
|
Form,
|
||||||
FormModel,
|
FormModel,
|
||||||
Dropdown,
|
Dropdown,
|
||||||
|
Captcha,
|
||||||
FormError
|
FormError
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { routeActions } from 'react-router-redux';
|
import { routeActions } from 'react-router-redux';
|
||||||
|
|
||||||
import request from 'services/request';
|
import request from 'services/request';
|
||||||
|
import captcha from 'services/captcha';
|
||||||
import accounts from 'services/api/accounts';
|
import accounts from 'services/api/accounts';
|
||||||
import authentication from 'services/api/authentication';
|
import authentication from 'services/api/authentication';
|
||||||
import { setLocale } from 'components/i18n/actions';
|
import { setLocale } from 'components/i18n/actions';
|
||||||
@ -27,6 +28,9 @@ export function changeLang(lang) {
|
|||||||
accounts.changeLang(lang);
|
accounts.changeLang(lang);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: probably should be moved from here, because it is side effect
|
||||||
|
captcha.setLang(lang);
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: CHANGE_LANG,
|
type: CHANGE_LANG,
|
||||||
payload: {
|
payload: {
|
||||||
|
@ -3,7 +3,7 @@ import { authenticate, changeLang } from 'components/user/actions';
|
|||||||
/**
|
/**
|
||||||
* Initializes User state with the fresh data
|
* Initializes User state with the fresh data
|
||||||
*
|
*
|
||||||
* @param {Object} store - redux store
|
* @param {object} store - redux store
|
||||||
*
|
*
|
||||||
* @return {Promise} a promise, that resolves in User state
|
* @return {Promise} a promise, that resolves in User state
|
||||||
*/
|
*/
|
||||||
|
@ -18,3 +18,25 @@ export function omit(obj, keys) {
|
|||||||
|
|
||||||
return newObj;
|
return newObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asynchronously loads script
|
||||||
|
*
|
||||||
|
* @param {string} src
|
||||||
|
*
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
export function loadScript(src) {
|
||||||
|
const script = document.createElement('script');
|
||||||
|
|
||||||
|
script.async = true;
|
||||||
|
script.defer = true;
|
||||||
|
script.src = src;
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
script.onlaod = resolve;
|
||||||
|
script.onerror = reject;
|
||||||
|
|
||||||
|
document.body.appendChild(script);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@ -11,6 +11,9 @@ import { IntlProvider } from 'components/i18n';
|
|||||||
import routesFactory from 'routes';
|
import routesFactory from 'routes';
|
||||||
import storeFactory from 'storeFactory';
|
import storeFactory from 'storeFactory';
|
||||||
import bsodFactory from 'components/ui/bsod/factory';
|
import bsodFactory from 'components/ui/bsod/factory';
|
||||||
|
import captcha from 'services/captcha';
|
||||||
|
|
||||||
|
captcha.setApiKey('6LdUZiYTAAAAAEjDGi9kEu0MRKYHYWskPFNXSYOV'); // TODO
|
||||||
|
|
||||||
const store = storeFactory();
|
const store = storeFactory();
|
||||||
|
|
||||||
|
@ -22,10 +22,10 @@ export default {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
resendActivation({email = ''}) {
|
resendActivation({email = '', captcha}) {
|
||||||
return request.post(
|
return request.post(
|
||||||
'/api/signup/repeat-message',
|
'/api/signup/repeat-message',
|
||||||
{email}
|
{email, captcha}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
59
src/services/captcha.js
Normal file
59
src/services/captcha.js
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import { loadScript } from 'functions';
|
||||||
|
|
||||||
|
let readyPromise;
|
||||||
|
let lang = 'en';
|
||||||
|
let sitekey;
|
||||||
|
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* @param {DOMNode|string} el - dom node or id of element where to render captcha
|
||||||
|
* @param {string} options.skin - skin color (dark|light)
|
||||||
|
* @param {function} options.onSetCode - the callback, that will be called with
|
||||||
|
* captcha verification code, after user successfully solves captcha
|
||||||
|
*
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
render(el, {skin: theme, onSetCode: callback}) {
|
||||||
|
if (!sitekey) {
|
||||||
|
throw new Error('Site key is required to render captcha');
|
||||||
|
}
|
||||||
|
|
||||||
|
return loadApi().then(() =>
|
||||||
|
window.grecaptcha.render(el, {
|
||||||
|
sitekey,
|
||||||
|
theme,
|
||||||
|
callback
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {stirng} newLang
|
||||||
|
*
|
||||||
|
* @see https://developers.google.com/recaptcha/docs/language
|
||||||
|
*/
|
||||||
|
setLang(newLang) {
|
||||||
|
lang = newLang;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} apiKey
|
||||||
|
*
|
||||||
|
* @see http://www.google.com/recaptcha/admin
|
||||||
|
*/
|
||||||
|
setApiKey(apiKey) {
|
||||||
|
sitekey = apiKey;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function loadApi() {
|
||||||
|
if (!readyPromise) {
|
||||||
|
readyPromise = new Promise((resolve) => {
|
||||||
|
window.onReCaptchaReady = resolve;
|
||||||
|
});
|
||||||
|
|
||||||
|
loadScript(`https://www.google.com/recaptcha/api.js?onload=onReCaptchaReady&render=explicit&hl=${lang}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return readyPromise;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user