From fde1d36287aab14f8f89cac1edb498ba914ab3d3 Mon Sep 17 00:00:00 2001 From: SleepWalker Date: Wed, 18 Apr 2018 22:49:10 +0300 Subject: [PATCH] #397: ensure, that recaptcha.render will be called with an existing el ref --- src/components/ui/form/Button.js | 24 ++++++------- src/components/ui/form/Captcha.js | 36 ++++++++++++-------- src/components/ui/form/FormComponent.js | 9 +++-- src/components/ui/form/FormInputComponent.js | 19 ++++++----- src/components/ui/form/FormModel.js | 2 +- src/services/captcha.js | 19 +++++++---- 6 files changed, 59 insertions(+), 50 deletions(-) diff --git a/src/components/ui/form/Button.js b/src/components/ui/form/Button.js index dcbdfbb..91d081a 100644 --- a/src/components/ui/form/Button.js +++ b/src/components/ui/form/Button.js @@ -1,25 +1,21 @@ // @flow +import type { MessageDescriptor } from 'react-intl'; import React from 'react'; - import classNames from 'classnames'; - import buttons from 'components/ui/buttons.scss'; import { COLOR_GREEN } from 'components/ui'; +import type { Color } from 'components/ui'; import FormComponent from './FormComponent'; -import type { Color } from 'components/ui'; - -export default class Button extends FormComponent { - props: { - label: string | {id: string}, - block: bool, - small: bool, - loading: bool, - className: string, - color: Color - }; - +export default class Button extends FormComponent<{ + label: string | MessageDescriptor, + block: bool, + small: bool, + loading: bool, + className: string, + color: Color +}> { static defaultProps = { color: COLOR_GREEN }; diff --git a/src/components/ui/form/Captcha.js b/src/components/ui/form/Captcha.js index e7717a8..2cad790 100644 --- a/src/components/ui/form/Captcha.js +++ b/src/components/ui/form/Captcha.js @@ -1,35 +1,41 @@ -import PropTypes from 'prop-types'; +// @flow import React from 'react'; - import classNames from 'classnames'; - +import type { CaptchaID } from 'services/captcha'; +import type { Skin } from 'components/ui'; import captcha from 'services/captcha'; import logger from 'services/logger'; -import { skins, SKIN_DARK } from 'components/ui'; import { ComponentLoader } from 'components/ui/loader'; import styles from './form.scss'; import FormInputComponent from './FormInputComponent'; -export default class Captcha extends FormInputComponent { - static displayName = 'Captcha'; - - static propTypes = { - skin: PropTypes.oneOf(skins), - delay: PropTypes.number - }; +export default class Captcha extends FormInputComponent<{ + delay: number, + skin: Skin, +}, { + code: string, +}> { + el: ?HTMLDivElement; + captchaId: CaptchaID; static defaultProps = { - skin: SKIN_DARK, + skin: 'dark', delay: 0 }; componentDidMount() { setTimeout(() => { - captcha.render(this.el, { + this.el && captcha.render(this.el, { skin: this.props.skin, onSetCode: this.setCode - }).then((captchaId) => this.captchaId = captchaId, (error) => logger.error('Error rendering captcha', { error })); + }) + .then((captchaId) => {this.captchaId = captchaId;}) + .catch((error) => { + logger.error('Failed rendering captcha', { + error + }); + }); }, this.props.delay); } @@ -64,5 +70,5 @@ export default class Captcha extends FormInputComponent { this.reset(); } - setCode = (code) => this.setState({code}); + setCode = (code: string) => this.setState({code}); } diff --git a/src/components/ui/form/FormComponent.js b/src/components/ui/form/FormComponent.js index 2a77f3c..661d294 100644 --- a/src/components/ui/form/FormComponent.js +++ b/src/components/ui/form/FormComponent.js @@ -1,10 +1,9 @@ +// @flow +import type { MessageDescriptor } from 'react-intl'; import { Component } from 'react'; - import { intlShape } from 'react-intl'; -export default class FormComponent extends Component { - static displayName = 'FormComponent'; - +export default class FormComponent extends Component { static contextTypes = { intl: intlShape.isRequired }; @@ -16,7 +15,7 @@ export default class FormComponent extends Component { * * @return {string} */ - formatMessage(message) { + formatMessage(message: string | MessageDescriptor) { if (message && message.id && this.context && this.context.intl) { message = this.context.intl.formatMessage(message); } diff --git a/src/components/ui/form/FormInputComponent.js b/src/components/ui/form/FormInputComponent.js index 515c91e..1e48bdd 100644 --- a/src/components/ui/form/FormInputComponent.js +++ b/src/components/ui/form/FormInputComponent.js @@ -1,15 +1,18 @@ -import PropTypes from 'prop-types'; +// @flow +import type { MessageDescriptor } from 'react-intl'; import React from 'react'; import FormComponent from './FormComponent'; import FormError from './FormError'; -export default class FormInputComponent extends FormComponent { - static displayName = 'FormInputComponent'; +type Error = string | MessageDescriptor; - static propTypes = { - error: PropTypes.string - }; +export default class FormInputComponent extends FormComponent

{ + el: ?HTMLDivElement; componentWillReceiveProps() { if (this.state && this.state.error) { @@ -19,7 +22,7 @@ export default class FormInputComponent extends FormComponent { } } - setEl = (el) => { + setEl = (el: ?HTMLDivElement) => { this.el = el; }; @@ -29,7 +32,7 @@ export default class FormInputComponent extends FormComponent { return ; } - setError(error) { + setError(error: Error) { this.setState({error}); } } diff --git a/src/components/ui/form/FormModel.js b/src/components/ui/form/FormModel.js index 8a92b42..955787e 100644 --- a/src/components/ui/form/FormModel.js +++ b/src/components/ui/form/FormModel.js @@ -32,7 +32,7 @@ export default class FormModel { const props: Object = { name, - ref: (el: ?FormInputComponent) => { + ref: (el: ?FormInputComponent) => { if (el) { if (!(el instanceof FormInputComponent)) { throw new Error('Expected FormInputComponent component'); diff --git a/src/services/captcha.js b/src/services/captcha.js index b823809..bed1492 100644 --- a/src/services/captcha.js +++ b/src/services/captcha.js @@ -1,3 +1,4 @@ +// @flow import { loadScript } from 'functions'; import options from 'services/api/options'; @@ -5,6 +6,8 @@ let readyPromise; let lang = 'en'; let sitekey; +export opaque type CaptchaID = string; + export default { /** * @param {DOMNode|string} el - dom node or id of element where to render captcha @@ -14,7 +17,10 @@ export default { * * @return {Promise} - resolves to captchaId */ - render(el, {skin: theme, onSetCode: callback}) { + render(el: HTMLElement, {skin: theme, onSetCode: callback}: { + skin: 'dark' | 'light', + onSetCode: (string) => void, + }): Promise { return this.loadApi().then(() => window.grecaptcha.render(el, { sitekey, @@ -27,7 +33,7 @@ export default { /** * @param {string} captchaId - captcha id, returned from render promise */ - reset(captchaId) { + reset(captchaId: CaptchaID) { this.loadApi().then(() => window.grecaptcha.reset(captchaId)); }, @@ -36,7 +42,7 @@ export default { * * @see https://developers.google.com/recaptcha/docs/language */ - setLang(newLang) { + setLang(newLang: string) { lang = newLang; }, @@ -45,7 +51,7 @@ export default { * * @see http://www.google.com/recaptcha/admin */ - setApiKey(apiKey) { + setApiKey(apiKey: string) { sitekey = apiKey; }, @@ -54,14 +60,14 @@ export default { * * @return {Promise} */ - loadApi() { + loadApi(): Promise { if (!readyPromise) { readyPromise = Promise.all([ new Promise((resolve) => { window.onReCaptchaReady = resolve; }), options.get().then((resp) => this.setApiKey(resp.reCaptchaPublicKey)) - ]); + ]).then(() => {}); loadScript(`https://recaptcha.net/recaptcha/api.js?onload=onReCaptchaReady&render=explicit&hl=${lang}`); } @@ -69,4 +75,3 @@ export default { return readyPromise; } }; -