Skip to content

Commit ce7627f

Browse files
committed
🚸 (gmail) Improve OAuth credential popup handling
1 parent bac1ee2 commit ce7627f

File tree

6 files changed

+324
-113
lines changed

6 files changed

+324
-113
lines changed

.cursor/rules/general.mdc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ alwaysApply: true
1010

1111
- Never use `any` type. Always use proper TypeScript types, interfaces, or union types instead.
1212
- Use `satisfies` instead of `as` when possible to make sure we keep strong type-safety.
13-
- Prefer writing long and complex functions that provide good, deep abstraction. These functions should have a TSDoc comment description of what it does in great details. Avoid providing `@param` description, params name should be self-explanatory. In the function itself you can add inline comments only if the code is hard to understand.
13+
- Prefer writing long and complex functions that provide good, deep abstraction. These functions should have a TSDoc comment description of what it does in great details. Avoid providing `@param` description, params name should be self-explanatory. IMPORTANT: Only add inline comments if a piece of logic is hard to understand, it should ideally explain what happens in the next N lines of code, never add a comment to explain a single line.
14+
- Prefer infer the return type of a function instead of declaring it.
1415
- Helper functions should be placed at the bottom of the file.
1516

1617
## Testing

apps/builder/src/features/forge/components/credentials/CreateForgedOAuthCredentialsModal.tsx

Lines changed: 32 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { stringify } from "querystring";
21
import { CopyButton } from "@/components/CopyButton";
32
import { TextInput } from "@/components/inputs/TextInput";
43
import { useWorkspace } from "@/features/workspace/WorkspaceProvider";
@@ -21,6 +20,7 @@ import { useMutation } from "@tanstack/react-query";
2120
import type { ForgedBlockDefinition } from "@typebot.io/forge-repository/definitions";
2221
import { Button } from "@typebot.io/ui/components/Button";
2322
import { useState } from "react";
23+
import { useOAuthPopup } from "./useOAuthPopup";
2424

2525
type Props = {
2626
blockDef: ForgedBlockDefinition;
@@ -69,7 +69,6 @@ export const CreateForgedOAuthCredentialsModalContent = ({
6969
"blockDef" | "onNewCredentials" | "defaultData" | "editorContext" | "scope"
7070
>) => {
7171
const { workspace } = useWorkspace();
72-
const [isAuthorizing, setIsAuthorizing] = useState(false);
7372
const [name, setName] = useState("");
7473
const [tab, setTab] = useState<"default" | "your-app">(
7574
blockDef.auth && "defaultClientEnvKeys" in blockDef.auth
@@ -95,54 +94,42 @@ export const CreateForgedOAuthCredentialsModalContent = ({
9594
}),
9695
);
9796

98-
const openOAuthPopup = async () => {
97+
const handleOAuthSuccess = (code: string) => {
9998
if (!workspace) return;
100-
101-
setIsAuthorizing(true);
102-
103-
window.open(
104-
`/api/${blockDef.id}/oauth/authorize?${stringify({
105-
clientId: clientId,
106-
})}`,
107-
"oauthPopup",
108-
"width=500,height=700",
109-
);
110-
111-
const handleOAuthResponse = (event: MessageEvent) => {
112-
if (event.data?.type === "oauth") {
113-
window.removeEventListener("message", handleOAuthResponse);
114-
setIsAuthorizing(false);
115-
const { code } = event.data;
116-
const credentials = {
117-
name,
118-
blockType: blockDef.id,
119-
code,
120-
customClient:
121-
tab === "your-app"
122-
? {
123-
id: clientId,
124-
secret: clientSecret,
125-
}
126-
: undefined,
127-
};
128-
mutate(
129-
scope === "workspace"
130-
? {
131-
...credentials,
132-
scope: "workspace",
133-
workspaceId: workspace.id,
134-
}
135-
: {
136-
...credentials,
137-
scope: "user",
138-
},
139-
);
140-
}
99+
const credentials = {
100+
name: name.trim(),
101+
blockType: blockDef.id,
102+
code,
103+
customClient:
104+
tab === "your-app"
105+
? {
106+
id: clientId.trim(),
107+
secret: clientSecret.trim(),
108+
}
109+
: undefined,
141110
};
142111

143-
window.addEventListener("message", handleOAuthResponse);
112+
mutate(
113+
scope === "workspace"
114+
? {
115+
...credentials,
116+
scope: "workspace",
117+
workspaceId: workspace.id,
118+
}
119+
: {
120+
...credentials,
121+
scope: "user",
122+
},
123+
);
144124
};
145125

126+
const { openOAuthPopup, isAuthorizing } = useOAuthPopup({
127+
blockId: blockDef.id,
128+
clientId,
129+
workspace: workspace ?? null,
130+
onSuccess: handleOAuthSuccess,
131+
});
132+
146133
if (!blockDef.auth) return null;
147134
return (
148135
<ModalContent>

apps/builder/src/features/forge/components/credentials/UpdateForgedOAuthCredentialsModalContent.tsx

Lines changed: 25 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { stringify } from "querystring";
21
import { CopyButton } from "@/components/CopyButton";
32
import { TextInput } from "@/components/inputs/TextInput";
43
import { useWorkspace } from "@/features/workspace/WorkspaceProvider";
@@ -19,6 +18,7 @@ import { useMutation, useQuery } from "@tanstack/react-query";
1918
import type { ForgedBlockDefinition } from "@typebot.io/forge-repository/definitions";
2019
import { Button } from "@typebot.io/ui/components/Button";
2120
import { useEffect, useState } from "react";
21+
import { useOAuthPopup } from "./useOAuthPopup";
2222

2323
type Props = {
2424
credentialsId: string;
@@ -96,42 +96,31 @@ export const UpdateForgedOAuthCredentialsModalContent = ({
9696
}),
9797
);
9898

99-
const openOAuthPopup = async () => {
99+
const handleOAuthSuccess = (code: string) => {
100100
if (!workspace) return;
101-
102-
window.open(
103-
`/api/${blockDef.id}/oauth/authorize?${stringify({
104-
clientId: clientId,
105-
})}`,
106-
"oauthPopup",
107-
"width=500,height=700",
108-
);
109-
110-
const handleOAuthResponse = (event: MessageEvent) => {
111-
if (event.data?.type === "oauth") {
112-
window.removeEventListener("message", handleOAuthResponse);
113-
const { code } = event.data;
114-
mutate({
115-
name,
116-
blockType: blockDef.id,
117-
workspaceId: workspace.id,
118-
credentialsId,
119-
code,
120-
customClient:
121-
tab === "your-app"
122-
? {
123-
id: clientId,
124-
secret: clientSecret,
125-
}
126-
: undefined,
127-
});
128-
}
129-
};
130-
131-
window.removeEventListener("message", handleOAuthResponse);
132-
window.addEventListener("message", handleOAuthResponse);
101+
mutate({
102+
name,
103+
blockType: blockDef.id,
104+
workspaceId: workspace.id,
105+
credentialsId,
106+
code,
107+
customClient:
108+
tab === "your-app"
109+
? {
110+
id: clientId,
111+
secret: clientSecret,
112+
}
113+
: undefined,
114+
});
133115
};
134116

117+
const { openOAuthPopup, isAuthorizing } = useOAuthPopup({
118+
blockId: blockDef.id,
119+
clientId,
120+
workspace: workspace ?? null,
121+
onSuccess: handleOAuthSuccess,
122+
});
123+
135124
if (!blockDef.auth) return null;
136125
return (
137126
<ModalContent>
@@ -196,7 +185,8 @@ export const UpdateForgedOAuthCredentialsModalContent = ({
196185
disabled={
197186
!name ||
198187
isPending ||
199-
(tab === "your-app" && (!clientId || !clientSecret))
188+
(tab === "your-app" && (!clientId || !clientSecret)) ||
189+
isAuthorizing
200190
}
201191
>
202192
<blockDef.LightLogo />

0 commit comments

Comments
 (0)