Fix grecaptcha throwing useless error sometimes

pull/21833/head
Dariusz Niemczyk 2021-07-19 15:41:44 +02:00
parent 863f4d4f9f
commit 9de5ebd4de
No known key found for this signature in database
GPG Key ID: 0AD2F70C94CA5B03
2 changed files with 54 additions and 30 deletions

View File

@ -90,6 +90,7 @@ declare global {
mxUIStore: UIStore; mxUIStore: UIStore;
mxSetupEncryptionStore?: SetupEncryptionStore; mxSetupEncryptionStore?: SetupEncryptionStore;
mxRoomScrollStateStore?: RoomScrollStateStore; mxRoomScrollStateStore?: RoomScrollStateStore;
mxOnRecaptchaLoaded?: () => void;
} }
interface Document { interface Document {
@ -114,7 +115,7 @@ declare global {
} }
interface StorageEstimate { interface StorageEstimate {
usageDetails?: {[key: string]: number}; usageDetails?: { [key: string]: number };
} }
interface HTMLAudioElement { interface HTMLAudioElement {
@ -185,4 +186,19 @@ declare global {
parameterDescriptors?: AudioParamDescriptor[]; parameterDescriptors?: AudioParamDescriptor[];
} }
); );
// eslint-disable-next-line no-var
var grecaptcha:
| undefined
| {
reset: (id: string) => void;
render: (
divId: string,
options: {
sitekey: string;
callback: () => void;
}
) => string;
isReady: () => boolean;
};
} }

View File

@ -15,55 +15,56 @@ limitations under the License.
*/ */
import React, { createRef } from 'react'; import React, { createRef } from 'react';
import PropTypes from 'prop-types';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import CountlyAnalytics from "../../../CountlyAnalytics"; import CountlyAnalytics from "../../../CountlyAnalytics";
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
const DIV_ID = 'mx_recaptcha'; const DIV_ID = 'mx_recaptcha';
interface ICaptchaFormProps {
sitePublicKey: string
onCaptchaResponse: () => void
}
interface ICaptchaFormState {
errorText: string | null
}
/** /**
* A pure UI component which displays a captcha form. * A pure UI component which displays a captcha form.
*/ */
@replaceableComponent("views.auth.CaptchaForm") @replaceableComponent("views.auth.CaptchaForm")
export default class CaptchaForm extends React.Component { export default class CaptchaForm extends React.Component<ICaptchaFormProps, ICaptchaFormState> {
static propTypes = {
sitePublicKey: PropTypes.string,
// called with the captcha response
onCaptchaResponse: PropTypes.func,
};
static defaultProps = { static defaultProps = {
onCaptchaResponse: () => {}, onCaptchaResponse: () => { },
}; };
constructor(props) { private _captchaWidgetId: string | null = null;
private _recaptchaContainer: React.RefObject<HTMLDivElement> = createRef();
state = {
errorText: null,
};
constructor(props: ICaptchaFormProps) {
super(props); super(props);
this.state = {
errorText: null,
};
this._captchaWidgetId = null;
this._recaptchaContainer = createRef();
CountlyAnalytics.instance.track("onboarding_grecaptcha_begin"); CountlyAnalytics.instance.track("onboarding_grecaptcha_begin");
} }
componentDidMount() { componentDidMount() {
// Just putting a script tag into the returned jsx doesn't work, annoyingly, // Just putting a script tag into the returned jsx doesn't work, annoyingly,
// so we do this instead. // so we do this instead.
if (global.grecaptcha) { if (this.isRecaptchaReady()) {
// already loaded // already loaded
this._onCaptchaLoaded(); this._onCaptchaLoaded();
} else { } else {
console.log("Loading recaptcha script..."); console.log("Loading recaptcha script...");
window.mx_on_recaptcha_loaded = () => {this._onCaptchaLoaded();}; window.mxOnRecaptchaLoaded = () => { this._onCaptchaLoaded(); };
const scriptTag = document.createElement('script'); const scriptTag = document.createElement('script');
scriptTag.setAttribute( scriptTag.setAttribute(
'src', `https://www.recaptcha.net/recaptcha/api.js?onload=mx_on_recaptcha_loaded&render=explicit`, 'src', `https://www.recaptcha.net/recaptcha/api.js?onload=mxOnRecaptchaLoaded&render=explicit`,
); );
this._recaptchaContainer.current.appendChild(scriptTag); this._recaptchaContainer.current.appendChild(scriptTag);
} }
@ -73,8 +74,15 @@ export default class CaptchaForm extends React.Component {
this._resetRecaptcha(); this._resetRecaptcha();
} }
_renderRecaptcha(divId) { // Borrowed directly from: https://github.com/codeep/react-recaptcha-google/commit/e118fa5670fa268426969323b2e7fe77698376ba
if (!global.grecaptcha) { private isRecaptchaReady() {
return typeof window !== "undefined" &&
typeof global.grecaptcha !== "undefined" &&
typeof global.grecaptcha.render === 'function';
}
private _renderRecaptcha(divId) {
if (!this.isRecaptchaReady()) {
console.error("grecaptcha not loaded!"); console.error("grecaptcha not loaded!");
throw new Error("Recaptcha did not load successfully"); throw new Error("Recaptcha did not load successfully");
} }
@ -84,7 +92,7 @@ export default class CaptchaForm extends React.Component {
console.error("No public key for recaptcha!"); console.error("No public key for recaptcha!");
throw new Error( throw new Error(
"This server has not supplied enough information for Recaptcha " "This server has not supplied enough information for Recaptcha "
+ "authentication"); + "authentication");
} }
console.info("Rendering to %s", divId); console.info("Rendering to %s", divId);
@ -94,13 +102,13 @@ export default class CaptchaForm extends React.Component {
}); });
} }
_resetRecaptcha() { private _resetRecaptcha() {
if (this._captchaWidgetId !== null) { if (this._captchaWidgetId !== null) {
global.grecaptcha.reset(this._captchaWidgetId); global.grecaptcha.reset(this._captchaWidgetId);
} }
} }
_onCaptchaLoaded() { private _onCaptchaLoaded() {
console.log("Loaded recaptcha script."); console.log("Loaded recaptcha script.");
try { try {
this._renderRecaptcha(DIV_ID); this._renderRecaptcha(DIV_ID);
@ -122,7 +130,7 @@ export default class CaptchaForm extends React.Component {
if (this.state.errorText) { if (this.state.errorText) {
error = ( error = (
<div className="error"> <div className="error">
{ this.state.errorText } {this.state.errorText}
</div> </div>
); );
} }
@ -133,7 +141,7 @@ export default class CaptchaForm extends React.Component {
"This homeserver would like to make sure you are not a robot.", "This homeserver would like to make sure you are not a robot.",
)}</p> )}</p>
<div id={DIV_ID} /> <div id={DIV_ID} />
{ error } {error}
</div> </div>
); );
} }