Skip to content

Commit fc2b051

Browse files
committed
refactor: handle cases when grecaptcha failed to load
1 parent 16f3da0 commit fc2b051

File tree

7 files changed

+81
-14
lines changed

7 files changed

+81
-14
lines changed

frontend/src/ts/controllers/captcha-controller.ts

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,27 @@ const siteKey = envConfig.recaptchaSiteKey;
66

77
const captchas: Record<string, number> = {};
88

9+
type Grecaptcha = {
10+
render: (
11+
element: HTMLElement,
12+
options: { sitekey: string; callback?: (responseToken: string) => void }
13+
) => number;
14+
reset: (widgetId: number) => void;
15+
getResponse: (widgetId: number) => string;
16+
};
17+
18+
function getGrecaptcha(): Grecaptcha {
19+
if (!("grecaptcha" in window)) {
20+
throw new Error("grecaptcha is not defined");
21+
}
22+
23+
return window.grecaptcha as Grecaptcha;
24+
}
25+
26+
export function isCaptchaAvailable(): boolean {
27+
return "grecaptcha" in window;
28+
}
29+
930
export function render(
1031
element: HTMLElement,
1132
id: string,
@@ -14,32 +35,23 @@ export function render(
1435
if (captchas[id] !== undefined && captchas[id] !== null) {
1536
return;
1637
}
17-
18-
//@ts-expect-error 3rd party code, no types
19-
const widgetId = grecaptcha.render(element, {
38+
const widgetId = getGrecaptcha().render(element, {
2039
sitekey: siteKey,
2140
callback,
2241
});
23-
24-
captchas[id] = widgetId as number;
42+
captchas[id] = widgetId;
2543
}
2644

2745
export function reset(id: string): void {
2846
if (captchas[id] === undefined || captchas[id] === null) {
2947
return;
3048
}
31-
32-
//@ts-expect-error 3rd party code, no types
33-
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
34-
grecaptcha.reset(captchas[id]);
49+
getGrecaptcha().reset(captchas[id]);
3550
}
3651

3752
export function getResponse(id: string): string {
3853
if (captchas[id] === undefined || captchas[id] === null) {
3954
return "";
4055
}
41-
42-
//@ts-expect-error 3rd party code, no types
43-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call
44-
return grecaptcha.getResponse(captchas[id]);
56+
return getGrecaptcha().getResponse(captchas[id]);
4557
}

frontend/src/ts/modals/forgot-password.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@ import * as Loader from "../elements/loader";
66
import { z } from "zod";
77

88
export function show(): void {
9+
if (!CaptchaController.isCaptchaAvailable()) {
10+
Notifications.add(
11+
"Could not show forgot password popup: Captcha is not available. This could happen due to a blocked or failed network request. Please refresh the page or contact support if this issue persists.",
12+
-1
13+
);
14+
return;
15+
}
16+
917
void modal.show({
1018
mode: "dialog",
1119
focusFirstInput: true,

frontend/src/ts/modals/google-sign-up.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,27 @@ function show(credential: UserCredential): void {
2323
mode: "dialog",
2424
focusFirstInput: true,
2525
beforeAnimation: async () => {
26+
signedInUser = credential;
27+
28+
if (!CaptchaController.isCaptchaAvailable()) {
29+
Notifications.add(
30+
"Could not show google sign up popup: Captcha is not avilable. This could happen due to a blocked or failed network request. Please refresh the page or contact support if this issue persists.",
31+
-1
32+
);
33+
return;
34+
}
2635
CaptchaController.reset("googleSignUpModal");
2736
CaptchaController.render(
2837
$("#googleSignUpModal .captcha")[0] as HTMLElement,
2938
"googleSignUpModal"
3039
);
3140
enableInput();
3241
disableButton();
33-
signedInUser = credential;
42+
},
43+
afterAnimation: async () => {
44+
if (!CaptchaController.isCaptchaAvailable()) {
45+
void hide();
46+
}
3447
},
3548
});
3649
}

frontend/src/ts/modals/quote-report.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ export async function show(
2424
quoteId: number,
2525
showOptions?: ShowOptions
2626
): Promise<void> {
27+
if (!CaptchaController.isCaptchaAvailable()) {
28+
Notifications.add(
29+
"Could not show quote report popup: Captcha is not available. This could happen due to a blocked or failed network request. Please refresh the page or contact support if this issue persists.",
30+
-1
31+
);
32+
return;
33+
}
34+
2735
void modal.show({
2836
mode: "dialog",
2937
...showOptions,

frontend/src/ts/modals/quote-submit.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,14 @@ async function submitQuote(): Promise<void> {
5353
}
5454

5555
export async function show(showOptions: ShowOptions): Promise<void> {
56+
if (!CaptchaController.isCaptchaAvailable()) {
57+
Notifications.add(
58+
"Could not show quote submit popup: Captcha is not available. This could happen due to a blocked or failed network request. Please refresh the page or contact support if this issue persists.",
59+
-1
60+
);
61+
return;
62+
}
63+
5664
void modal.show({
5765
...showOptions,
5866
mode: "dialog",

frontend/src/ts/modals/register-captcha.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
11
import * as CaptchaController from "../controllers/captcha-controller";
22
import AnimatedModal from "../utils/animated-modal";
33
import { promiseWithResolvers } from "../utils/misc";
4+
import * as Notifications from "../elements/notifications";
45

56
let { promise, resolve } = promiseWithResolvers<string | undefined>();
67

78
export { promise };
89

910
export async function show(): Promise<void> {
11+
if (!CaptchaController.isCaptchaAvailable()) {
12+
Notifications.add(
13+
"Could not show register popup: Captcha is not available. This could happen due to a blocked or failed network request. Please refresh the page or contact support if this issue persists.",
14+
-1
15+
);
16+
resolve(undefined);
17+
return;
18+
}
19+
1020
await modal.show({
1121
mode: "dialog",
1222
beforeAnimation: async (modal) => {

frontend/src/ts/modals/user-report.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@ export async function show(options: ShowOptions): Promise<void> {
3232
return;
3333
}
3434

35+
if (!CaptchaController.isCaptchaAvailable()) {
36+
Notifications.add(
37+
"Could not show user report popup: Captcha is not available. This could happen due to a blocked or failed network request. Please refresh the page or contact support if this issue persists.",
38+
-1
39+
);
40+
return;
41+
}
42+
3543
void modal.show({
3644
mode: "dialog",
3745
focusFirstInput: true,

0 commit comments

Comments
 (0)