import { useState, useEffect } from "react"; import { Button } from "@/components/ui/button"; import { Github } from "lucide-react"; import { IpcClient } from "@/ipc/ipc_client"; import { useSettings } from "@/hooks/useSettings"; import { useLoadApp } from "@/hooks/useLoadApp"; interface GitHubConnectorProps { appId: number | null; folderName: string; } export function GitHubConnector({ appId, folderName }: GitHubConnectorProps) { // --- GitHub Device Flow State --- const { app, refreshApp } = useLoadApp(appId); const { settings, refreshSettings } = useSettings(); const [githubUserCode, setGithubUserCode] = useState(null); const [githubVerificationUri, setGithubVerificationUri] = useState< string | null >(null); const [githubError, setGithubError] = useState(null); const [isConnectingToGithub, setIsConnectingToGithub] = useState(false); const [githubStatusMessage, setGithubStatusMessage] = useState( null ); // --- --- const handleConnectToGithub = async () => { if (!appId) return; setIsConnectingToGithub(true); setGithubError(null); setGithubUserCode(null); setGithubVerificationUri(null); setGithubStatusMessage("Requesting device code from GitHub..."); // Send IPC message to main process to start the flow IpcClient.getInstance().startGithubDeviceFlow(appId); }; useEffect(() => { if (!appId) return; // Don't set up listeners if appId is null initially const cleanupFunctions: (() => void)[] = []; // Listener for updates (user code, verification uri, status messages) const removeUpdateListener = IpcClient.getInstance().onGithubDeviceFlowUpdate((data) => { console.log("Received github:flow-update", data); if (data.userCode) { setGithubUserCode(data.userCode); } if (data.verificationUri) { setGithubVerificationUri(data.verificationUri); } if (data.message) { setGithubStatusMessage(data.message); } setGithubError(null); // Clear previous errors on new update if (!data.userCode && !data.verificationUri && data.message) { // Likely just a status message, keep connecting state setIsConnectingToGithub(true); } if (data.userCode && data.verificationUri) { setIsConnectingToGithub(true); // Still connecting until success/error } }); cleanupFunctions.push(removeUpdateListener); // Listener for success const removeSuccessListener = IpcClient.getInstance().onGithubDeviceFlowSuccess((data) => { console.log("Received github:flow-success", data); setGithubStatusMessage("Successfully connected to GitHub!"); setGithubUserCode(null); // Clear user-facing info setGithubVerificationUri(null); setGithubError(null); setIsConnectingToGithub(false); refreshSettings(); // TODO: Maybe update parent UI to show "Connected" state or trigger next action }); cleanupFunctions.push(removeSuccessListener); // Listener for errors const removeErrorListener = IpcClient.getInstance().onGithubDeviceFlowError( (data) => { console.log("Received github:flow-error", data); setGithubError(data.error || "An unknown error occurred."); setGithubStatusMessage(null); setGithubUserCode(null); setGithubVerificationUri(null); setIsConnectingToGithub(false); } ); cleanupFunctions.push(removeErrorListener); // Cleanup function to remove all listeners when component unmounts or appId changes return () => { cleanupFunctions.forEach((cleanup) => cleanup()); // Optional: Send a message to main process to cancel polling if component unmounts // Only cancel if we were actually connecting for this specific appId // IpcClient.getInstance().cancelGithubDeviceFlow(appId); // Reset state when appId changes or component unmounts setGithubUserCode(null); setGithubVerificationUri(null); setGithubError(null); setIsConnectingToGithub(false); setGithubStatusMessage(null); }; }, [appId]); // Re-run effect if appId changes // --- Create Repo State --- const [repoName, setRepoName] = useState(folderName); const [repoAvailable, setRepoAvailable] = useState(null); const [repoCheckError, setRepoCheckError] = useState(null); const [isCheckingRepo, setIsCheckingRepo] = useState(false); const [isCreatingRepo, setIsCreatingRepo] = useState(false); const [createRepoError, setCreateRepoError] = useState(null); const [createRepoSuccess, setCreateRepoSuccess] = useState(false); // --- Sync to GitHub State --- const [isSyncing, setIsSyncing] = useState(false); const [syncError, setSyncError] = useState(null); const [syncSuccess, setSyncSuccess] = useState(false); // Assume org is the authenticated user for now (could add org input later) // TODO: After device flow, fetch and store the GitHub username/org in settings for use here const githubOrg = ""; // Use empty string for now (GitHub API will default to the authenticated user) const handleRepoNameBlur = async () => { setRepoCheckError(null); setRepoAvailable(null); if (!repoName) return; setIsCheckingRepo(true); try { const result = await IpcClient.getInstance().checkGithubRepoAvailable( githubOrg, repoName ); setRepoAvailable(result.available); if (!result.available) { setRepoCheckError(result.error || "Repository name is not available."); } } catch (err: any) { setRepoCheckError(err.message || "Failed to check repo availability."); } finally { setIsCheckingRepo(false); } }; const handleCreateRepo = async (e: React.FormEvent) => { e.preventDefault(); setCreateRepoError(null); setIsCreatingRepo(true); setCreateRepoSuccess(false); try { const result = await IpcClient.getInstance().createGithubRepo( githubOrg, repoName, appId! ); if (result.success) { setCreateRepoSuccess(true); setRepoCheckError(null); refreshApp(); } else { setCreateRepoError(result.error || "Failed to create repository."); } } catch (err: any) { setCreateRepoError(err.message || "Failed to create repository."); } finally { setIsCreatingRepo(false); } }; if (!settings?.githubAccessToken) { return (
{" "} {/* GitHub Connection Status/Instructions */} {(githubUserCode || githubStatusMessage || githubError) && (

GitHub Connection

{githubError && (

Error: {githubError}

)} {githubUserCode && githubVerificationUri && ( )} {githubStatusMessage && (

{githubStatusMessage}

)}
)}
); } if (app?.githubOrg && app?.githubRepo) { const handleSyncToGithub = async () => { setIsSyncing(true); setSyncError(null); setSyncSuccess(false); try { const result = await IpcClient.getInstance().syncGithubRepo(appId!); if (result.success) { setSyncSuccess(true); } else { setSyncError(result.error || "Failed to sync to GitHub."); } } catch (err: any) { setSyncError(err.message || "Failed to sync to GitHub."); } finally { setIsSyncing(false); } }; return ( ); } else { return (

Set up your GitHub repo

{ setRepoName(e.target.value); setRepoAvailable(null); setRepoCheckError(null); }} onBlur={handleRepoNameBlur} disabled={isCreatingRepo} /> {isCheckingRepo && (

Checking availability...

)} {repoAvailable === true && (

Repository name is available!

)} {repoAvailable === false && (

{repoCheckError}

)}
{createRepoError && (

{createRepoError}

)} {createRepoSuccess && (

Repository created and linked!

)}
); } }