← vibesdk
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
};
}