Advertisement

← vibesdk

src/hooks/use-github-export.ts

7.9 KB Download file
import { useState, useCallback, useEffect } from 'react';
import { WebSocket } from 'partysocket';
import { useAuth } from '@/contexts/auth-context';
import { apiClient } from '@/lib/api-client';
import type {
    AuthUser,
    GitHubExportOptions,
    GitHubExportResult
} from '../api-types';
import type { WebSocketMessageData } from '@/api-types';

// Use existing backend WebSocket types
type GitHubExportProgress = WebSocketMessageData<'github_export_progress'>;

export interface GitHubExportState {
    isExporting: boolean;
    progress?: GitHubExportProgress;
    result?: GitHubExportResult;
    isModalOpen: boolean;
}

export interface GitHubInstallationData {
    installationId: number;
    username: string;
    repositories?: string[];
}

export type GitHubExportHook = ReturnType<typeof useGitHubExport>;

export function useGitHubExport(
    _websocket?: WebSocket | null, 
    agentId?: string,
    onSuccess?: () => void
): {
    isExporting: boolean;
    progress?: GitHubExportProgress;
    result?: GitHubExportResult;
    isModalOpen: boolean;
    openModal: () => void;
    closeModal: () => void;
    startExport: (options: GitHubExportOptions) => Promise<void>;
    isAuthenticated: boolean;
    user: AuthUser | null;
    retry: () => void;
} {
    const { user, isAuthenticated } = useAuth();
    const [state, setState] = useState<GitHubExportState>({
        isExporting: false,
        isModalOpen: false
    });

    // Open the export modal
    const openModal = useCallback(() => {
        setState(prev => ({
            ...prev,
            isModalOpen: true,
            result: undefined
        }));
    }, []);

    // Close the export modal
    const closeModal = useCallback(() => {
        setState(prev => ({
            ...prev,
            isModalOpen: false,
            isExporting: false,
            progress: undefined,
            result: undefined
        }));
    }, []);

    useEffect(() => {
        const urlParams = new URLSearchParams(window.location.search);
        const githubExport = urlParams.get('github_export');
        
        if (githubExport === 'success') {
            const repositoryUrl = urlParams.get('repository_url');
            setState(prev => ({
                ...prev,
                isExporting: false,
                isModalOpen: true,
                result: {
                    success: true,
                    repositoryUrl: repositoryUrl || undefined
                }
            }));
            
            onSuccess?.();
            const newUrl = new URL(window.location.href);
            newUrl.searchParams.delete('github_export');
            newUrl.searchParams.delete('repository_url');
            window.history.replaceState({}, '', newUrl.toString());
            
        } else if (githubExport === 'error') {
            const reason = urlParams.get('reason') || 'Unknown error';
            setState(prev => ({
                ...prev,
                isExporting: false,
                isModalOpen: true, // Auto-open modal to show error result
                result: {
                    success: false,
                    error: `GitHub export failed: ${reason}`
                }
            }));
            
            // Clean up URL
            const newUrl = new URL(window.location.href);
            newUrl.searchParams.delete('github_export');
            newUrl.searchParams.delete('reason');
            window.history.replaceState({}, '', newUrl.toString());
        }
    }, [onSuccess]);

    const startExport = useCallback(async (options: GitHubExportOptions) => {
        setState(prev => ({
            ...prev,
            isExporting: true,
            progress: { message: 'Initiating GitHub authorization...', step: 'creating_repository', progress: 10 },
            result: undefined
        }));

        try {
            if (!agentId) {
                setState(prev => ({
                    ...prev,
                    isExporting: false,
                    result: {
                        success: false,
                        error: 'Invalid chat session'
                    }
                }));
                return;
            }

            const response = await apiClient.initiateGitHubExport({
                repositoryName: options.repositoryName,
                description: options.description,
                isPrivate: options.isPrivate,
                agentId: agentId
            });

            if (response.data?.alreadyExists && response.data?.existingRepositoryUrl) {
                setState(prev => ({
                    ...prev,
                    isExporting: false,
                    result: {
                        success: false,
                        error: 'Repository already exists',
                        repositoryAlreadyExists: true,
                        existingRepositoryUrl: response.data?.existingRepositoryUrl || ''
                    }
                }));
                return;
            }
            if (response.data?.skippedOAuth && response.data?.repositoryUrl) {
                setState(prev => ({
                    ...prev,
                    isExporting: false,
                    result: {
                        success: true,
                        repositoryUrl: response.data?.repositoryUrl || ''
                    }
                }));
                onSuccess?.();
                return;
            }

            if (response.data?.authUrl) {
                setState(prev => ({
                    ...prev,
                    progress: { message: 'Redirecting to GitHub...', step: 'creating_repository', progress: 25 }
                }));
                setTimeout(() => {
                    window.location.href = response.data?.authUrl || '';
                }, 500);
            } else {
                setState(prev => ({
                    ...prev,
                    isExporting: false,
                    result: {
                        success: false,
                        error: 'Failed to initiate GitHub authorization'
                    }
                }));
            }
        } catch (error: unknown) {
            const apiError = error as { 
                response?: { 
                    data?: { 
                        alreadyExists?: boolean; 
                        existingRepositoryUrl?: string;
                    } 
                }; 
                data?: { 
                    alreadyExists?: boolean; 
                    existingRepositoryUrl?: string;
                }; 
                message?: string;
            };
            const errorData = apiError?.response?.data || apiError?.data;
            if (errorData?.alreadyExists && errorData?.existingRepositoryUrl) {
                setState(prev => ({
                    ...prev,
                    isExporting: false,
                    result: {
                        success: false,
                        error: 'Repository already exists',
                        repositoryAlreadyExists: true,
                        existingRepositoryUrl: errorData.existingRepositoryUrl
                    }
                }));
                return;
            }
            
            setState(prev => ({
                ...prev,
                isExporting: false,
                result: {
                    success: false,
                    error: apiError?.message || 'Failed to initiate GitHub export'
                }
            }));
        }
    }, [agentId, onSuccess]);
    const retry = useCallback(() => {
        setState(prev => ({
            ...prev,
            result: undefined,
            progress: undefined,
            isExporting: false
        }));
    }, []);

    return {
        ...state,
        openModal,
        closeModal,
        startExport,
        isAuthenticated,
        user,
        retry
    };
}