Skip to content

Instantly share code, notes, and snippets.

@kraftwerk28
Created May 24, 2022 07:44
Show Gist options
  • Save kraftwerk28/8dfd0c2beb7f352d8839122c596a5036 to your computer and use it in GitHub Desktop.
Save kraftwerk28/8dfd0c2beb7f352d8839122c596a5036 to your computer and use it in GitHub Desktop.
import { useRef, useState, useEffect, createContext, useContext } from 'react';
type ExecuteResponse = { key: string; response: string };
type HCaptchaContext = {
widgetId: string | undefined;
setWid(wId: string | undefined): void;
loading: boolean;
getToken(): Promise<string>;
setExecutePending(v: boolean): void;
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const hcaptchaContext = createContext<HCaptchaContext>(undefined as any);
export const HCaptchaProvider: React.FC = ({ children }) => {
const [widgetId, setWid] = useState<string | undefined>();
const [executePending, setExecutePending] = useState(false);
const getToken = async () => {
setExecutePending(true);
const r: ExecuteResponse = await window['hcaptcha'].execute(widgetId, {
async: true,
});
setExecutePending(false);
return r.response;
};
const loading = !widgetId || executePending;
const ctxValue = { widgetId, setWid, loading, getToken, setExecutePending };
return (
<hcaptchaContext.Provider value={ctxValue}>
{children}
</hcaptchaContext.Provider>
);
};
const hCaptchaSitekey = "...";
export function useHCaptcha() {
return useContext(hcaptchaContext);
}
type Props = React.HTMLAttributes<HTMLDivElement>;
export const HCaptcha: React.FC<Props> = (props) => {
const captchaContainer = useRef<HTMLDivElement>(null);
const captcha = useHCaptcha();
useEffect(() => {
const scriptId = 'hcaptcha-src';
function hcaptchaOnLoad() {
if (captcha.widgetId) {
return;
}
const widgetId = window['hcaptcha']?.render(captchaContainer.current, {
sitekey: hCaptchaSitekey,
size: 'invisible',
'close-callback'() {
captcha.setExecutePending(false);
},
'chalexpired-callback'() {
captcha.setExecutePending(false);
},
'error-callback'(err: string) {
// TODO: notify about the error
},
});
delete window[hcaptchaOnLoad.name];
captcha.setWid(widgetId);
}
if (document.head.querySelector(`script#${scriptId}`)) {
hcaptchaOnLoad();
} else {
Object.assign(window, { [hcaptchaOnLoad.name]: hcaptchaOnLoad });
const scriptTag = document.createElement('script');
scriptTag.src =
`https://js.hcaptcha.com/1/api.js` +
`?onload=${hcaptchaOnLoad.name}&render=explicit`;
scriptTag.id = scriptId;
scriptTag.async = true;
scriptTag.defer = true;
scriptTag.addEventListener('error', () => {
captcha.setWid('error');
// TODO: notify about the error
});
document.head.appendChild(scriptTag);
}
return () => {
if (captcha.widgetId) {
window['hcaptcha'].reset(captcha.widgetId);
captcha.setWid(undefined);
}
};
}, [captcha]);
return <div {...props} ref={captchaContainer} />;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment