Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Fix grecaptcha throwing useless error sometimes #6401

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion src/@types/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ declare global {
mxUIStore: UIStore;
mxSetupEncryptionStore?: SetupEncryptionStore;
mxRoomScrollStateStore?: RoomScrollStateStore;
mxOnRecaptchaLoaded?: () => void;
}

interface Document {
Expand All @@ -114,7 +115,7 @@ declare global {
}

interface StorageEstimate {
usageDetails?: {[key: string]: number};
usageDetails?: { [key: string]: number };
}

interface HTMLAudioElement {
Expand Down Expand Up @@ -185,4 +186,19 @@ declare global {
parameterDescriptors?: AudioParamDescriptor[];
}
);

// eslint-disable-next-line no-var
var grecaptcha:
| undefined
| {
reset: (id: string) => void;
render: (
divId: string,
options: {
sitekey: string;
callback: () => void;
}
Palid marked this conversation as resolved.
Show resolved Hide resolved
) => string;
isReady: () => boolean;
};
t3chguy marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,55 +15,56 @@ limitations under the License.
*/

import React, { createRef } from 'react';
import PropTypes from 'prop-types';
import { _t } from '../../../languageHandler';
import CountlyAnalytics from "../../../CountlyAnalytics";
import { replaceableComponent } from "../../../utils/replaceableComponent";

const DIV_ID = 'mx_recaptcha';

interface ICaptchaFormProps {
sitePublicKey: string
onCaptchaResponse: () => void
Palid marked this conversation as resolved.
Show resolved Hide resolved
}

interface ICaptchaFormState {
errorText: string | null

Palid marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* A pure UI component which displays a captcha form.
*/
@replaceableComponent("views.auth.CaptchaForm")
export default class CaptchaForm extends React.Component {
static propTypes = {
sitePublicKey: PropTypes.string,

// called with the captcha response
onCaptchaResponse: PropTypes.func,
};

export default class CaptchaForm extends React.Component<ICaptchaFormProps, ICaptchaFormState> {
static defaultProps = {
onCaptchaResponse: () => {},
onCaptchaResponse: () => { },
Palid marked this conversation as resolved.
Show resolved Hide resolved
};

constructor(props) {
super(props);

this.state = {
errorText: null,
};
private _captchaWidgetId: string | null = null;
private _recaptchaContainer: React.RefObject<HTMLDivElement> = createRef();
Palid marked this conversation as resolved.
Show resolved Hide resolved

this._captchaWidgetId = null;
state = {
errorText: null,
};
Palid marked this conversation as resolved.
Show resolved Hide resolved

this._recaptchaContainer = createRef();
constructor(props: ICaptchaFormProps) {
super(props);

CountlyAnalytics.instance.track("onboarding_grecaptcha_begin");
}

componentDidMount() {
// Just putting a script tag into the returned jsx doesn't work, annoyingly,
// so we do this instead.
if (global.grecaptcha) {
if (this.isRecaptchaReady()) {
// already loaded
this._onCaptchaLoaded();
} else {
console.log("Loading recaptcha script...");
window.mx_on_recaptcha_loaded = () => {this._onCaptchaLoaded();};
window.mxOnRecaptchaLoaded = () => { this._onCaptchaLoaded(); };
const scriptTag = document.createElement('script');
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);
}
Expand All @@ -73,8 +74,15 @@ export default class CaptchaForm extends React.Component {
this._resetRecaptcha();
}

_renderRecaptcha(divId) {
if (!global.grecaptcha) {
// Borrowed directly from: https://github.com/codeep/react-recaptcha-google/commit/e118fa5670fa268426969323b2e7fe77698376ba
private isRecaptchaReady() {
Palid marked this conversation as resolved.
Show resolved Hide resolved
return typeof window !== "undefined" &&
typeof global.grecaptcha !== "undefined" &&
typeof global.grecaptcha.render === 'function';
}

private _renderRecaptcha(divId) {
Palid marked this conversation as resolved.
Show resolved Hide resolved
if (!this.isRecaptchaReady()) {
console.error("grecaptcha not loaded!");
throw new Error("Recaptcha did not load successfully");
}
Expand All @@ -84,7 +92,7 @@ export default class CaptchaForm extends React.Component {
console.error("No public key for recaptcha!");
throw new Error(
"This server has not supplied enough information for Recaptcha "
+ "authentication");
+ "authentication");
}

console.info("Rendering to %s", divId);
Expand All @@ -94,13 +102,13 @@ export default class CaptchaForm extends React.Component {
});
}

_resetRecaptcha() {
private _resetRecaptcha() {
Palid marked this conversation as resolved.
Show resolved Hide resolved
if (this._captchaWidgetId !== null) {
global.grecaptcha.reset(this._captchaWidgetId);
}
}

_onCaptchaLoaded() {
private _onCaptchaLoaded() {
Palid marked this conversation as resolved.
Show resolved Hide resolved
console.log("Loaded recaptcha script.");
try {
this._renderRecaptcha(DIV_ID);
Expand All @@ -122,7 +130,7 @@ export default class CaptchaForm extends React.Component {
if (this.state.errorText) {
error = (
<div className="error">
{ this.state.errorText }
{this.state.errorText}
Palid marked this conversation as resolved.
Show resolved Hide resolved
</div>
);
}
Expand All @@ -133,7 +141,7 @@ export default class CaptchaForm extends React.Component {
"This homeserver would like to make sure you are not a robot.",
)}</p>
<div id={DIV_ID} />
{ error }
{error}
Palid marked this conversation as resolved.
Show resolved Hide resolved
</div>
);
}
Expand Down