History

Share OAuth sign-in button UI (#451)

Rhys Sullivan committed 6 months ago

Commit d8df88b1 parent 9638cc34 files · +52 −39Browse files

Unified · Split

4 files
  1. packages/plugins/google-discovery/src/react/GoogleDiscoverySignInButton.tsx +9 −13
  2. packages/plugins/graphql/src/react/GraphqlSignInButton.tsx +7 −13
  3. packages/plugins/mcp/src/react/McpSignInButton.tsx +9 −13
  4. packages/react/src/plugins/oauth-sign-in.tsx +27 −0
packages/plugins/google-discovery/src/react/GoogleDiscoverySignInButton.tsx+9−13
@@ -4,8 +4,8 @@ import { useAtomSet, useAtomValue, Result } from "@effect-atom/atom-react"; · 3 unmodified lines
4import { connectionsAtom } from "@executor/react/api/atoms";4import { connectionsAtom } from "@executor/react/api/atoms";
5import { useScope } from "@executor/react/api/scope-context";5import { useScope } from "@executor/react/api/scope-context";
6import { connectionWriteKeys, sourceWriteKeys } from "@executor/react/api/reactivity-keys";6import { connectionWriteKeys, sourceWriteKeys } from "@executor/react/api/reactivity-keys";
7import { Button } from "@executor/react/components/button";
8import {7import {
8 OAuthSignInButton,
9 oauthCallbackUrl,9 oauthCallbackUrl,
10 useOAuthPopupFlow,10 useOAuthPopupFlow,
11 type OAuthCompletionPayload,11 type OAuthCompletionPayload,
@@ -96,17 +96,13 @@ export default function GoogleDiscoverySignInButton(props: { sourceId: string }) · 84 unmodified lines
96 if (!oauth2) return null;96 if (!oauth2) return null;
97 97
98 return (98 return (
99 <div className="flex items-center gap-2">99 <OAuthSignInButton
100 {oauth.error && <span className="text-xs text-destructive">{oauth.error}</span>}100 busy={oauth.busy}
101 <Button variant="outline" size="sm" onClick={() => void handleSignIn()} disabled={oauth.busy}>101 error={oauth.error}
102 {oauth.busy102 isConnected={isConnected}
103 ? isConnected103 onSignIn={() => void handleSignIn()}
104 ? "Reconnecting…"104 reconnectingLabel="Reconnecting…"
105 : "Signing in…"105 signingInLabel="Signing in…"
106 : isConnected106 />
107 ? "Reconnect"
108 : "Sign in"}
109 </Button>
110 </div>
111 );107 );
112}108}
packages/plugins/graphql/src/react/GraphqlSignInButton.tsx+7−13
@@ -4,8 +4,8 @@ import { useAtomSet, useAtomValue, Result } from "@effect-atom/atom-react"; · 3 unmodified lines
4import { connectionsAtom } from "@executor/react/api/atoms";4import { connectionsAtom } from "@executor/react/api/atoms";
5import { useScope } from "@executor/react/api/scope-context";5import { useScope } from "@executor/react/api/scope-context";
6import { sourceWriteKeys } from "@executor/react/api/reactivity-keys";6import { sourceWriteKeys } from "@executor/react/api/reactivity-keys";
7import { Button } from "@executor/react/components/button";
8import {7import {
8 OAuthSignInButton,
9 oauthCallbackUrl,9 oauthCallbackUrl,
10 oauthConnectionId,10 oauthConnectionId,
11 useOAuthPopupFlow,11 useOAuthPopupFlow,
@@ -64,17 +64,11 @@ export default function GraphqlSignInButton(props: { sourceId: string }) { · 52 unmodified lines
64 if (!oauth2) return null;64 if (!oauth2) return null;
65 65
66 return (66 return (
67 <div className="flex items-center gap-2">67 <OAuthSignInButton
68 {oauth.error && <span className="text-xs text-destructive">{oauth.error}</span>}68 busy={oauth.busy}
69 <Button variant="outline" size="sm" onClick={() => void handleSignIn()} disabled={oauth.busy}>69 error={oauth.error}
70 {oauth.busy70 isConnected={isConnected}
71 ? isConnected71 onSignIn={() => void handleSignIn()}
72 ? "Reconnecting..."72 />
73 : "Signing in..."
74 : isConnected
75 ? "Reconnect"
76 : "Sign in"}
77 </Button>
78 </div>
79 );73 );
80}74}
packages/plugins/mcp/src/react/McpSignInButton.tsx+9−13
@@ -4,8 +4,8 @@ import { useAtomSet, useAtomValue, Result } from "@effect-atom/atom-react"; · 3 unmodified lines
4import { useScope } from "@executor/react/api/scope-context";4import { useScope } from "@executor/react/api/scope-context";
5import { sourceWriteKeys } from "@executor/react/api/reactivity-keys";5import { sourceWriteKeys } from "@executor/react/api/reactivity-keys";
6import { connectionsAtom } from "@executor/react/api/atoms";6import { connectionsAtom } from "@executor/react/api/atoms";
7import { Button } from "@executor/react/components/button";
8import {7import {
8 OAuthSignInButton,
9 oauthCallbackUrl,9 oauthCallbackUrl,
10 oauthConnectionId,10 oauthConnectionId,
11 useOAuthPopupFlow,11 useOAuthPopupFlow,
@@ -75,17 +75,13 @@ export default function McpSignInButton(props: { sourceId: string }) { · 63 unmodified lines
75 if (!oauth2) return null;75 if (!oauth2) return null;
76 76
77 return (77 return (
78 <div className="flex items-center gap-2">78 <OAuthSignInButton
79 {oauth.error && <span className="text-xs text-destructive">{oauth.error}</span>}79 busy={oauth.busy}
80 <Button variant="outline" size="sm" onClick={() => void handleSignIn()} disabled={oauth.busy}>80 error={oauth.error}
81 {oauth.busy81 isConnected={isConnected}
82 ? isConnected82 onSignIn={() => void handleSignIn()}
83 ? "Reconnecting…"83 reconnectingLabel="Reconnecting…"
84 : "Signing in…"84 signingInLabel="Signing in…"
85 : isConnected85 />
86 ? "Reconnect"
87 : "Sign in"}
88 </Button>
89 </div>
90 );86 );
91}87}
packages/react/src/plugins/oauth-sign-in.tsx+27−0
@@ -4,6 +4,7 @@ import { useAtomSet } from "@effect-atom/atom-react"; · 3 unmodified lines
4import { cancelOAuth, startOAuth } from "../api/atoms";4import { cancelOAuth, startOAuth } from "../api/atoms";
5import { openOAuthPopup, type OAuthPopupResult } from "../api/oauth-popup";5import { openOAuthPopup, type OAuthPopupResult } from "../api/oauth-popup";
6import { useScope } from "../api/scope-context";6import { useScope } from "../api/scope-context";
7import { Button } from "../components/button";
7import { OAUTH_POPUP_MESSAGE_TYPE, type OAuthStrategy } from "@executor/sdk";8import { OAUTH_POPUP_MESSAGE_TYPE, type OAuthStrategy } from "@executor/sdk";
8 9
9type SecretBackedValue =10type SecretBackedValue =
@@ -231,3 +232,29 @@ export function useOAuthPopupFlow< · 221 unmodified lines
231 cancel,232 cancel,
232 };233 };
233}234}
235
236export function OAuthSignInButton(props: {
237 readonly busy: boolean;
238 readonly error: string | null;
239 readonly isConnected: boolean;
240 readonly onSignIn: () => void;
241 readonly reconnectingLabel?: string;
242 readonly signingInLabel?: string;
243 readonly reconnectLabel?: string;
244 readonly signInLabel?: string;
245}) {
246 return (
247 <div className="flex items-center gap-2">
248 {props.error && <span className="text-xs text-destructive">{props.error}</span>}
249 <Button variant="outline" size="sm" onClick={props.onSignIn} disabled={props.busy}>
250 {props.busy
251 ? props.isConnected
252 ? (props.reconnectingLabel ?? "Reconnecting...")
253 : (props.signingInLabel ?? "Signing in...")
254 : props.isConnected
255 ? (props.reconnectLabel ?? "Reconnect")
256 : (props.signInLabel ?? "Sign in")}
257 </Button>
258 </div>
259 );
260}