Unified · Split
| @@ -4,8 +4,8 @@ import { useAtomSet, useAtomValue, Result } from "@effect-atom/atom-react"; · 3 unmodified lines | |||
| 4 | import { connectionsAtom } from "@executor/react/api/atoms"; | 4 | import { connectionsAtom } from "@executor/react/api/atoms"; |
| 5 | import { useScope } from "@executor/react/api/scope-context"; | 5 | import { useScope } from "@executor/react/api/scope-context"; |
| 6 | import { connectionWriteKeys, sourceWriteKeys } from "@executor/react/api/reactivity-keys"; | 6 | import { connectionWriteKeys, sourceWriteKeys } from "@executor/react/api/reactivity-keys"; |
| 7 | import { Button } from "@executor/react/components/button"; | ||
| 8 | import { | 7 | import { |
| 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.busy | 102 | isConnected={isConnected} |
| 103 | ? isConnected | 103 | onSignIn={() => void handleSignIn()} |
| 104 | ? "Reconnecting…" | 104 | reconnectingLabel="Reconnecting…" |
| 105 | : "Signing in…" | 105 | signingInLabel="Signing in…" |
| 106 | : isConnected | 106 | /> |
| 107 | ? "Reconnect" | ||
| 108 | : "Sign in"} | ||
| 109 | </Button> | ||
| 110 | </div> | ||
| 111 | ); | 107 | ); |
| 112 | } | 108 | } |
| @@ -4,8 +4,8 @@ import { useAtomSet, useAtomValue, Result } from "@effect-atom/atom-react"; · 3 unmodified lines | |||
| 4 | import { connectionsAtom } from "@executor/react/api/atoms"; | 4 | import { connectionsAtom } from "@executor/react/api/atoms"; |
| 5 | import { useScope } from "@executor/react/api/scope-context"; | 5 | import { useScope } from "@executor/react/api/scope-context"; |
| 6 | import { sourceWriteKeys } from "@executor/react/api/reactivity-keys"; | 6 | import { sourceWriteKeys } from "@executor/react/api/reactivity-keys"; |
| 7 | import { Button } from "@executor/react/components/button"; | ||
| 8 | import { | 7 | import { |
| 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.busy | 70 | isConnected={isConnected} |
| 71 | ? isConnected | 71 | onSignIn={() => void handleSignIn()} |
| 72 | ? "Reconnecting..." | 72 | /> |
| 73 | : "Signing in..." | ||
| 74 | : isConnected | ||
| 75 | ? "Reconnect" | ||
| 76 | : "Sign in"} | ||
| 77 | </Button> | ||
| 78 | </div> | ||
| 79 | ); | 73 | ); |
| 80 | } | 74 | } |
| @@ -4,8 +4,8 @@ import { useAtomSet, useAtomValue, Result } from "@effect-atom/atom-react"; · 3 unmodified lines | |||
| 4 | import { useScope } from "@executor/react/api/scope-context"; | 4 | import { useScope } from "@executor/react/api/scope-context"; |
| 5 | import { sourceWriteKeys } from "@executor/react/api/reactivity-keys"; | 5 | import { sourceWriteKeys } from "@executor/react/api/reactivity-keys"; |
| 6 | import { connectionsAtom } from "@executor/react/api/atoms"; | 6 | import { connectionsAtom } from "@executor/react/api/atoms"; |
| 7 | import { Button } from "@executor/react/components/button"; | ||
| 8 | import { | 7 | import { |
| 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.busy | 81 | isConnected={isConnected} |
| 82 | ? isConnected | 82 | onSignIn={() => void handleSignIn()} |
| 83 | ? "Reconnecting…" | 83 | reconnectingLabel="Reconnecting…" |
| 84 | : "Signing in…" | 84 | signingInLabel="Signing in…" |
| 85 | : isConnected | 85 | /> |
| 86 | ? "Reconnect" | ||
| 87 | : "Sign in"} | ||
| 88 | </Button> | ||
| 89 | </div> | ||
| 90 | ); | 86 | ); |
| 91 | } | 87 | } |
| @@ -4,6 +4,7 @@ import { useAtomSet } from "@effect-atom/atom-react"; · 3 unmodified lines | |||
| 4 | import { cancelOAuth, startOAuth } from "../api/atoms"; | 4 | import { cancelOAuth, startOAuth } from "../api/atoms"; |
| 5 | import { openOAuthPopup, type OAuthPopupResult } from "../api/oauth-popup"; | 5 | import { openOAuthPopup, type OAuthPopupResult } from "../api/oauth-popup"; |
| 6 | import { useScope } from "../api/scope-context"; | 6 | import { useScope } from "../api/scope-context"; |
| 7 | import { Button } from "../components/button"; | ||
| 7 | import { OAUTH_POPUP_MESSAGE_TYPE, type OAuthStrategy } from "@executor/sdk"; | 8 | import { OAUTH_POPUP_MESSAGE_TYPE, type OAuthStrategy } from "@executor/sdk"; |
| 8 | 9 | ||
| 9 | type SecretBackedValue = | 10 | type SecretBackedValue = |
| @@ -231,3 +232,29 @@ export function useOAuthPopupFlow< · 221 unmodified lines | |||
| 231 | cancel, | 232 | cancel, |
| 232 | }; | 233 | }; |
| 233 | } | 234 | } |
| 235 | |||
| 236 | export 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 | } | ||