From 9de5ebd4def72816fb5afa0f4546df6ea2a69a52 Mon Sep 17 00:00:00 2001 From: Dariusz Niemczyk Date: Mon, 19 Jul 2021 15:41:44 +0200 Subject: [PATCH 1/5] Fix grecaptcha throwing useless error sometimes --- src/@types/global.d.ts | 18 ++++- .../auth/{CaptchaForm.js => CaptchaForm.tsx} | 66 +++++++++++-------- 2 files changed, 54 insertions(+), 30 deletions(-) rename src/components/views/auth/{CaptchaForm.js => CaptchaForm.tsx} (73%) diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index 7192eb81cc..f485d753c5 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -90,6 +90,7 @@ declare global { mxUIStore: UIStore; mxSetupEncryptionStore?: SetupEncryptionStore; mxRoomScrollStateStore?: RoomScrollStateStore; + mxOnRecaptchaLoaded?: () => void; } interface Document { @@ -114,7 +115,7 @@ declare global { } interface StorageEstimate { - usageDetails?: {[key: string]: number}; + usageDetails?: { [key: string]: number }; } interface HTMLAudioElement { @@ -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; + } + ) => string; + isReady: () => boolean; + }; } diff --git a/src/components/views/auth/CaptchaForm.js b/src/components/views/auth/CaptchaForm.tsx similarity index 73% rename from src/components/views/auth/CaptchaForm.js rename to src/components/views/auth/CaptchaForm.tsx index bea4f89f53..878cedc13f 100644 --- a/src/components/views/auth/CaptchaForm.js +++ b/src/components/views/auth/CaptchaForm.tsx @@ -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 +} + +interface ICaptchaFormState { + errorText: string | null + +} + /** * 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 { static defaultProps = { - onCaptchaResponse: () => {}, + onCaptchaResponse: () => { }, }; - constructor(props) { + private _captchaWidgetId: string | null = null; + private _recaptchaContainer: React.RefObject = createRef(); + + state = { + errorText: null, + }; + + constructor(props: ICaptchaFormProps) { super(props); - this.state = { - errorText: null, - }; - - this._captchaWidgetId = null; - - this._recaptchaContainer = createRef(); - 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); } @@ -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() { + return typeof window !== "undefined" && + typeof global.grecaptcha !== "undefined" && + typeof global.grecaptcha.render === 'function'; + } + + private _renderRecaptcha(divId) { + if (!this.isRecaptchaReady()) { console.error("grecaptcha not loaded!"); 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!"); throw new Error( "This server has not supplied enough information for Recaptcha " - + "authentication"); + + "authentication"); } console.info("Rendering to %s", divId); @@ -94,13 +102,13 @@ export default class CaptchaForm extends React.Component { }); } - _resetRecaptcha() { + private _resetRecaptcha() { if (this._captchaWidgetId !== null) { global.grecaptcha.reset(this._captchaWidgetId); } } - _onCaptchaLoaded() { + private _onCaptchaLoaded() { console.log("Loaded recaptcha script."); try { this._renderRecaptcha(DIV_ID); @@ -122,7 +130,7 @@ export default class CaptchaForm extends React.Component { if (this.state.errorText) { error = (
- { this.state.errorText } + {this.state.errorText}
); } @@ -133,7 +141,7 @@ export default class CaptchaForm extends React.Component { "This homeserver would like to make sure you are not a robot.", )}

- { error } + {error}
); } From c2394b9161b08761a9abe38fb9a313c0b6aee4c9 Mon Sep 17 00:00:00 2001 From: Dariusz Niemczyk Date: Mon, 19 Jul 2021 16:27:39 +0200 Subject: [PATCH 2/5] Fix styling comments --- src/@types/global.d.ts | 2 +- src/components/views/auth/CaptchaForm.tsx | 48 +++++++++++------------ 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index f485d753c5..46c23ff8d8 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -196,7 +196,7 @@ declare global { divId: string, options: { sitekey: string; - callback: () => void; + callback: (response: string) => void; } ) => string; isReady: () => boolean; diff --git a/src/components/views/auth/CaptchaForm.tsx b/src/components/views/auth/CaptchaForm.tsx index 878cedc13f..749d6dbd39 100644 --- a/src/components/views/auth/CaptchaForm.tsx +++ b/src/components/views/auth/CaptchaForm.tsx @@ -22,12 +22,12 @@ import { replaceableComponent } from "../../../utils/replaceableComponent"; const DIV_ID = 'mx_recaptcha'; interface ICaptchaFormProps { - sitePublicKey: string - onCaptchaResponse: () => void + sitePublicKey: string; + onCaptchaResponse: (response: string) => void; } interface ICaptchaFormState { - errorText: string | null + errorText: string | null; } @@ -37,19 +37,19 @@ interface ICaptchaFormState { @replaceableComponent("views.auth.CaptchaForm") export default class CaptchaForm extends React.Component { static defaultProps = { - onCaptchaResponse: () => { }, + onCaptchaResponse: () => {}, }; - private _captchaWidgetId: string | null = null; - private _recaptchaContainer: React.RefObject = createRef(); - - state = { - errorText: null, - }; + private captchaWidgetId: string | null = null; + private recaptchaContainer = createRef(); constructor(props: ICaptchaFormProps) { super(props); + this.state = { + errorText: null, + }; + CountlyAnalytics.instance.track("onboarding_grecaptcha_begin"); } @@ -58,30 +58,30 @@ export default class CaptchaForm extends React.Component { this._onCaptchaLoaded(); }; + window.mxOnRecaptchaLoaded = () => { this.onCaptchaLoaded(); }; const scriptTag = document.createElement('script'); scriptTag.setAttribute( 'src', `https://www.recaptcha.net/recaptcha/api.js?onload=mxOnRecaptchaLoaded&render=explicit`, ); - this._recaptchaContainer.current.appendChild(scriptTag); + this.recaptchaContainer.current.appendChild(scriptTag); } } componentWillUnmount() { - this._resetRecaptcha(); + this.resetRecaptcha(); } // Borrowed directly from: https://github.com/codeep/react-recaptcha-google/commit/e118fa5670fa268426969323b2e7fe77698376ba - private isRecaptchaReady() { + private isRecaptchaReady(): boolean { return typeof window !== "undefined" && typeof global.grecaptcha !== "undefined" && typeof global.grecaptcha.render === 'function'; } - private _renderRecaptcha(divId) { + private renderRecaptcha(divId: string) { if (!this.isRecaptchaReady()) { console.error("grecaptcha not loaded!"); throw new Error("Recaptcha did not load successfully"); @@ -96,22 +96,22 @@ export default class CaptchaForm extends React.Component +

{_t( "This homeserver would like to make sure you are not a robot.", )}

- {error} + { error }
); } From 97d490c1b63845f2ee81f11573f37aff7ae2feda Mon Sep 17 00:00:00 2001 From: Dariusz Niemczyk <3636685+Palid@users.noreply.github.com> Date: Mon, 19 Jul 2021 16:28:30 +0200 Subject: [PATCH 3/5] Update src/components/views/auth/CaptchaForm.tsx Co-authored-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/auth/CaptchaForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/auth/CaptchaForm.tsx b/src/components/views/auth/CaptchaForm.tsx index 749d6dbd39..4934382fdb 100644 --- a/src/components/views/auth/CaptchaForm.tsx +++ b/src/components/views/auth/CaptchaForm.tsx @@ -130,7 +130,7 @@ export default class CaptchaForm extends React.Component - {this.state.errorText} + { this.state.errorText }
); } From 9e9992078baa2756e2e26f22106afbd8f3119828 Mon Sep 17 00:00:00 2001 From: Dariusz Niemczyk <3636685+Palid@users.noreply.github.com> Date: Mon, 19 Jul 2021 16:34:07 +0200 Subject: [PATCH 4/5] Update src/@types/global.d.ts Co-authored-by: Michael Telatynski <7t3chguy@gmail.com> --- src/@types/global.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index 46c23ff8d8..8bf6329f80 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -197,7 +197,7 @@ declare global { options: { sitekey: string; callback: (response: string) => void; - } + }, ) => string; isReady: () => boolean; }; From 5acc7fe8d4e8394a82f39fbe17a0ee95106415f2 Mon Sep 17 00:00:00 2001 From: Dariusz Niemczyk Date: Mon, 19 Jul 2021 16:49:35 +0200 Subject: [PATCH 5/5] Make `errorText` properly optional instead of null --- src/components/views/auth/CaptchaForm.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/views/auth/CaptchaForm.tsx b/src/components/views/auth/CaptchaForm.tsx index 4934382fdb..14237b2404 100644 --- a/src/components/views/auth/CaptchaForm.tsx +++ b/src/components/views/auth/CaptchaForm.tsx @@ -27,7 +27,7 @@ interface ICaptchaFormProps { } interface ICaptchaFormState { - errorText: string | null; + errorText?: string; } @@ -40,14 +40,14 @@ export default class CaptchaForm extends React.Component {}, }; - private captchaWidgetId: string | null = null; + private captchaWidgetId?: string; private recaptchaContainer = createRef(); constructor(props: ICaptchaFormProps) { super(props); this.state = { - errorText: null, + errorText: undefined, }; CountlyAnalytics.instance.track("onboarding_grecaptcha_begin");