TypeScriptify useRecaptcha
This commit is contained in:
		
					parent
					
						
							
								9a44790450
							
						
					
				
			
			
				commit
				
					
						af74228f8e
					
				
			
		
					 3 changed files with 34 additions and 33 deletions
				
			
		|  | @ -8,7 +8,7 @@ | |||
|     "build-storybook": "build-storybook", | ||||
|     "prettier:check": "prettier -c src", | ||||
|     "prettier:format": "prettier -w src", | ||||
|     "lint:js": "eslint --max-warnings 2 src", | ||||
|     "lint:js": "eslint --max-warnings 0 src", | ||||
|     "lint:types": "tsc" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|  | @ -31,6 +31,7 @@ | |||
|     "@react-stately/tree": "^3.2.0", | ||||
|     "@sentry/react": "^6.13.3", | ||||
|     "@sentry/tracing": "^6.13.3", | ||||
|     "@types/grecaptcha": "^3.0.4", | ||||
|     "@use-gesture/react": "^10.2.11", | ||||
|     "classnames": "^2.3.1", | ||||
|     "color-hash": "^2.0.1", | ||||
|  |  | |||
|  | @ -14,52 +14,49 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| import { randomString } from "matrix-js-sdk/src/randomstring"; | ||||
| import { useEffect, useCallback, useRef, useState } from "react"; | ||||
| import { randomString } from "matrix-js-sdk/src/randomstring"; | ||||
| 
 | ||||
| declare global { | ||||
|   interface Window { | ||||
|     mxOnRecaptchaLoaded: () => void; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| const RECAPTCHA_SCRIPT_URL = | ||||
|   "https://www.recaptcha.net/recaptcha/api.js?onload=mxOnRecaptchaLoaded&render=explicit"; | ||||
| 
 | ||||
| export function useRecaptcha(sitekey) { | ||||
| interface RecaptchaPromiseRef { | ||||
|   resolve: (response: string) => void; | ||||
|   reject: (error: Error) => void; | ||||
| } | ||||
| 
 | ||||
| export const useRecaptcha = (sitekey: string) => { | ||||
|   const [recaptchaId] = useState(() => randomString(16)); | ||||
|   const promiseRef = useRef(); | ||||
|   const promiseRef = useRef<RecaptchaPromiseRef>(); | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     if (!sitekey) { | ||||
|       return; | ||||
|     } | ||||
|     if (!sitekey) return; | ||||
| 
 | ||||
|     const onRecaptchaLoaded = () => { | ||||
|       if (!document.getElementById(recaptchaId)) { | ||||
|         return; | ||||
|       } | ||||
|       if (!document.getElementById(recaptchaId)) return; | ||||
| 
 | ||||
|       window.grecaptcha.render(recaptchaId, { | ||||
|         sitekey, | ||||
|         size: "invisible", | ||||
|         callback: (response) => { | ||||
|           if (promiseRef.current) { | ||||
|             promiseRef.current.resolve(response); | ||||
|           } | ||||
|         }, | ||||
|         "error-callback": (error) => { | ||||
|           if (promiseRef.current) { | ||||
|             promiseRef.current.reject(error); | ||||
|           } | ||||
|         }, | ||||
|         callback: (response: string) => promiseRef.current?.resolve(response), | ||||
|         // eslint-disable-next-line @typescript-eslint/naming-convention
 | ||||
|         "error-callback": () => promiseRef.current?.reject(new Error()), | ||||
|       }); | ||||
|     }; | ||||
| 
 | ||||
|     if ( | ||||
|       typeof window.grecaptcha !== "undefined" && | ||||
|       typeof window.grecaptcha.render === "function" | ||||
|     ) { | ||||
|     if (typeof window.grecaptcha?.render === "function") { | ||||
|       onRecaptchaLoaded(); | ||||
|     } else { | ||||
|       window.mxOnRecaptchaLoaded = onRecaptchaLoaded; | ||||
| 
 | ||||
|       if (!document.querySelector(`script[src="${RECAPTCHA_SCRIPT_URL}"]`)) { | ||||
|         const scriptTag = document.createElement("script"); | ||||
|         const scriptTag = document.createElement("script") as HTMLScriptElement; | ||||
|         scriptTag.src = RECAPTCHA_SCRIPT_URL; | ||||
|         scriptTag.async = true; | ||||
|         document.body.appendChild(scriptTag); | ||||
|  | @ -80,7 +77,7 @@ export function useRecaptcha(sitekey) { | |||
|     return new Promise((resolve, reject) => { | ||||
|       const observer = new MutationObserver((mutationsList) => { | ||||
|         for (const item of mutationsList) { | ||||
|           if (item.target?.style?.visibility !== "visible") { | ||||
|           if ((item.target as HTMLElement)?.style?.visibility !== "visible") { | ||||
|             reject(new Error("Recaptcha dismissed")); | ||||
|             observer.disconnect(); | ||||
|             return; | ||||
|  | @ -101,7 +98,7 @@ export function useRecaptcha(sitekey) { | |||
| 
 | ||||
|       window.grecaptcha.execute(); | ||||
| 
 | ||||
|       const iframe = document.querySelector( | ||||
|       const iframe = document.querySelector<HTMLIFrameElement>( | ||||
|         'iframe[src*="recaptcha/api2/bframe"]' | ||||
|       ); | ||||
| 
 | ||||
|  | @ -111,13 +108,11 @@ export function useRecaptcha(sitekey) { | |||
|         }); | ||||
|       } | ||||
|     }); | ||||
|   }, [recaptchaId, sitekey]); | ||||
|   }, [sitekey]); | ||||
| 
 | ||||
|   const reset = useCallback(() => { | ||||
|     if (window.grecaptcha) { | ||||
|       window.grecaptcha.reset(); | ||||
|     } | ||||
|   }, [recaptchaId]); | ||||
|     window.grecaptcha?.reset(); | ||||
|   }, []); | ||||
| 
 | ||||
|   return { execute, reset, recaptchaId }; | ||||
| } | ||||
| }; | ||||
|  | @ -2876,6 +2876,11 @@ | |||
|     "@types/minimatch" "*" | ||||
|     "@types/node" "*" | ||||
| 
 | ||||
| "@types/grecaptcha@^3.0.4": | ||||
|   version "3.0.4" | ||||
|   resolved "https://registry.yarnpkg.com/@types/grecaptcha/-/grecaptcha-3.0.4.tgz#3de601f3b0cd0298faf052dd5bd62aff64c2be2e" | ||||
|   integrity sha512-7l1Y8DTGXkx/r4pwU1nMVAR+yD/QC+MCHKXAyEX/7JZhwcN1IED09aZ9vCjjkcGdhSQiu/eJqcXInpl6eEEEwg== | ||||
| 
 | ||||
| "@types/hast@^2.0.0": | ||||
|   version "2.3.4" | ||||
|   resolved "https://registry.yarnpkg.com/@types/hast/-/hast-2.3.4.tgz#8aa5ef92c117d20d974a82bdfb6a648b08c0bafc" | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue