import { ActionIcon, Alert, Button, Group, Modal, Stack, Text } from "@mantine/core"; import { IconBrandGithub } from "@tabler/icons-react"; import { useEffect, useState } from "react"; export interface GitHubAuth { configured: boolean; connected: boolean; login: string | null; error?: string; pending?: { userCode: string; verificationUrl: string; expiresAt: number }; } export function GitHubConnection({ auth, request, openLink, refresh, openSettings, openSignal = 0, inline = false, hideTrigger = false }: { openSignal?: number; inline?: boolean; hideTrigger?: boolean; auth: GitHubAuth; request: (action: "status" | "start" | "poll" | "disconnect") => Promise; openLink: (url: string) => Promise; refresh: () => Promise; openSettings: () => Promise; }) { const [opened, setOpened] = useState(inline); const [current, setCurrent] = useState(auth); const [busy, setBusy] = useState(false); const [error, setError] = useState(null); useEffect(() => { setCurrent(auth); }, [auth]); useEffect(() => { if (openSignal > 0) { setError(null); setOpened(true); } }, [openSignal]); useEffect(() => { if (!opened || !current.pending) return; let cancelled = false; let polling = false; const timer = window.setInterval(async () => { if (polling || document.visibilityState === "hidden") return; polling = true; try { const next = await request("poll"); if (cancelled) return; setCurrent(next); if (next.connected) await refresh(); } catch (cause) { if (!cancelled) { setError(String(cause)); setCurrent((value) => ({ ...value, pending: undefined })); } } finally { polling = false; } }, 5000); return () => { cancelled = true; window.clearInterval(timer); }; // Snapshot polling supplies a new pending object; the code itself stays stable. }, [opened, current.pending?.userCode, request, refresh]); async function action(operation: () => Promise) { setBusy(true); setError(null); try { await operation(); } catch (cause) { setError(String(cause)); } finally { setBusy(false); } } const controls = {(error || current.error) && {error || current.error}} {!current.configured ? <> GitHub connection is not ready. Open plugin settings and try again. : current.connected ? <> Connected as {current.login}. Install TaskChef in each organization you want to use, and select the repositories it can access. An organization owner may need to approve the installation. You can reconnect at any time. : current.pending ? <> Enter this code on GitHub: {current.pending.userCode} Waiting for your approval. Keep this dialog open. The code expires in 15 minutes. : <> Let TaskChef read PR and CI status for your selected repositories and update the board automatically. } ; if (inline) return controls; return <> {!hideTrigger && { setError(null); setOpened(true); }}>} setOpened(false)} title="GitHub PR status" centered>{controls} ; }