History

Merge remote-tracking branch 'origin/codex/batch16-react-oauth-popup-exit' into codex/effect-lint-integration

Rhys Sullivan committed 6 months ago

Commit 27459402 parents e5b1196 23439e61 file · +80 −66Browse files

Unified · Split

packages/react/src/plugins/oauth-sign-in.tsx+80−66
@@ -1,5 +1,7 @@
11 import { useCallback, useEffect, useRef, useState } from "react";
22 import { useAtomSet } from "@effect/atom-react";
3+import * as Effect from "effect/Effect";
4+import * as Exit from "effect/Exit";
35
46 import { cancelOAuth, startOAuth } from "../api/atoms";
57 import { openOAuthPopup, type OAuthPopupResult } from "../api/oauth-popup";
@@ -80,8 +82,8 @@ export function useOAuthPopupFlow< · 74 unmodified lines
8082 startErrorMessage,
8183 } = options;
8284 const scopeId = useScope();
83− const doStartOAuth = useAtomSet(startOAuth, { mode: "promise" });
84− const doCancelOAuth = useAtomSet(cancelOAuth, { mode: "promise" });
85+ const doStartOAuth = useAtomSet(startOAuth, { mode: "promiseExit" });
86+ const doCancelOAuth = useAtomSet(cancelOAuth, { mode: "promiseExit" });
8587 const [busy, setBusy] = useState(false);
8688 const [error, setError] = useState<string | null>(null);
8789 const cleanupRef = useRef<(() => void) | null>(null);
@@ -92,7 +94,7 @@ export function useOAuthPopupFlow< · 4 unmodified lines
9294 void doCancelOAuth({
9395 params: { scopeId },
9496 payload: { sessionId },
95− }).catch(() => undefined);
97+ });
9698 },
9799 [doCancelOAuth, scopeId],
98100 );
@@ -122,73 +124,81 @@ export function useOAuthPopupFlow< · 23 unmodified lines
122124 cancel();
123125 setBusy(true);
124126 setError(null);
125− try {
126− const response = await input.run();
127− if (response.authorizationUrl === null) {
128− const message =
129− noAuthorizationUrlMessage ?? "OAuth start did not produce an authorization URL";
130− setBusy(false);
131− setError(message);
132− input.onError?.(message);
133− return;
134− }
135−
136− sessionRef.current = response.sessionId;
137− input.onAuthorizationStarted?.(response);
138− cleanupRef.current = openOAuthPopup<TPayload>({
139− url: response.authorizationUrl,
140− popupName,
141− channelName: OAUTH_POPUP_MESSAGE_TYPE,
142− expectedSessionId: response.sessionId,
143− onResult: async (result: OAuthPopupResult<TPayload>) => {
144− cleanupRef.current = null;
145− sessionRef.current = null;
127+ const startExit = await Effect.runPromiseExit(
128+ Effect.tryPromise({
129+ try: input.run,
130+ catch: (cause) => cause,
131+ }),
132+ );
133+ if (Exit.isFailure(startExit)) {
134+ const message = startErrorMessage ?? "Failed to start sign-in";
135+ setBusy(false);
136+ setError(message);
137+ input.onError?.(message);
138+ return;
139+ }
140+ const response = startExit.value;
141+ if (response.authorizationUrl === null) {
142+ const message =
143+ noAuthorizationUrlMessage ?? "OAuth start did not produce an authorization URL";
144+ setBusy(false);
145+ setError(message);
146+ input.onError?.(message);
147+ return;
148+ }
146149
147− if (!result.ok) {
148− setBusy(false);
149− setError(result.error);
150− input.onError?.(result.error);
151− return;
152− }
150+ sessionRef.current = response.sessionId;
151+ input.onAuthorizationStarted?.(response);
152+ cleanupRef.current = openOAuthPopup<TPayload>({
153+ url: response.authorizationUrl,
154+ popupName,
155+ channelName: OAUTH_POPUP_MESSAGE_TYPE,
156+ expectedSessionId: response.sessionId,
157+ onResult: async (result: OAuthPopupResult<TPayload>) => {
158+ cleanupRef.current = null;
159+ sessionRef.current = null;
153160
154− try {
155− await input.onSuccess(result);
156− setBusy(false);
157− } catch (e) {
158− const message = e instanceof Error ? e.message : "Failed to persist new connection";
159− setBusy(false);
160− setError(message);
161− input.onError?.(message);
162− }
163− },
164− onClosed: () => {
165− cleanupRef.current = null;
166− sessionRef.current = null;
167− cancelSession(response.sessionId);
168− const message =
169− popupClosedMessage ??
170− "Sign-in cancelled - popup was closed before completing the flow.";
161+ if (!result.ok) {
171162 setBusy(false);
172− setError(message);
173− input.onError?.(message);
174− },
175− onOpenFailed: () => {
176− cleanupRef.current = null;
177− sessionRef.current = null;
178− cancelSession(response.sessionId);
179− const message = popupBlockedMessage ?? "Sign-in popup was blocked by the browser";
163+ setError(result.error);
164+ input.onError?.(result.error);
165+ return;
166+ }
167+
168+ const persisted = await Promise.resolve(input.onSuccess(result)).then(
169+ () => true,
170+ () => false,
171+ );
172+ if (!persisted) {
173+ const message = "Failed to persist new connection";
180174 setBusy(false);
181175 setError(message);
182176 input.onError?.(message);
183− },
184− });
185− } catch (e) {
186− const message =
187− e instanceof Error ? e.message : (startErrorMessage ?? "Failed to start sign-in");
188− setBusy(false);
189− setError(message);
190− input.onError?.(message);
191− }
177+ return;
178+ }
179+ setBusy(false);
180+ },
181+ onClosed: () => {
182+ cleanupRef.current = null;
183+ sessionRef.current = null;
184+ cancelSession(response.sessionId);
185+ const message =
186+ popupClosedMessage ??
187+ "Sign-in cancelled - popup was closed before completing the flow.";
188+ setBusy(false);
189+ setError(message);
190+ input.onError?.(message);
191+ },
192+ onOpenFailed: () => {
193+ cleanupRef.current = null;
194+ sessionRef.current = null;
195+ cancelSession(response.sessionId);
196+ const message = popupBlockedMessage ?? "Sign-in popup was blocked by the browser";
197+ setBusy(false);
198+ setError(message);
199+ input.onError?.(message);
200+ },
201+ });
192202 },
193203 [
194204 cancel,
@@ -214,10 +224,14 @@ export function useOAuthPopupFlow< · 19 unmodified lines
214224 ...input.payload,
215225 redirectUrl: input.payload.redirectUrl ?? oauthCallbackUrl(callbackPath),
216226 },
217− }),
227+ }).then((exit) =>
228+ Exit.isSuccess(exit)
229+ ? exit.value
230+ : Effect.runPromise(Effect.fail(startErrorMessage ?? "Failed to start sign-in")),
231+ ),
218232 });
219233 },
220− [callbackPath, doStartOAuth, openAuthorization, scopeId],
234+ [callbackPath, doStartOAuth, openAuthorization, scopeId, startErrorMessage],
221235 );
222236
223237 return {