Spaces:
Paused
Paused
| /** | |
| * GitHub Query Hooks | |
| * | |
| * React Query hooks for fetching GitHub issues, PRs, and validations. | |
| */ | |
| import { useQuery, useInfiniteQuery } from '@tanstack/react-query'; | |
| import { getElectronAPI } from '@/lib/electron'; | |
| import { queryKeys } from '@/lib/query-keys'; | |
| import { STALE_TIMES } from '@/lib/query-client'; | |
| import type { | |
| GitHubIssue, | |
| GitHubPR, | |
| GitHubComment, | |
| PRReviewComment, | |
| StoredValidation, | |
| } from '@/lib/electron'; | |
| interface GitHubIssuesResult { | |
| openIssues: GitHubIssue[]; | |
| closedIssues: GitHubIssue[]; | |
| } | |
| interface GitHubPRsResult { | |
| openPRs: GitHubPR[]; | |
| mergedPRs: GitHubPR[]; | |
| } | |
| /** | |
| * Fetch GitHub issues for a project | |
| * | |
| * @param projectPath - Path to the project | |
| * @returns Query result with open and closed issues | |
| * | |
| * @example | |
| * ```tsx | |
| * const { data, isLoading } = useGitHubIssues(currentProject?.path); | |
| * const { openIssues, closedIssues } = data ?? { openIssues: [], closedIssues: [] }; | |
| * ``` | |
| */ | |
| export function useGitHubIssues(projectPath: string | undefined) { | |
| return useQuery({ | |
| queryKey: queryKeys.github.issues(projectPath ?? ''), | |
| queryFn: async (): Promise<GitHubIssuesResult> => { | |
| if (!projectPath) throw new Error('No project path'); | |
| const api = getElectronAPI(); | |
| if (!api.github) { | |
| throw new Error('GitHub API not available'); | |
| } | |
| const result = await api.github.listIssues(projectPath); | |
| if (!result.success) { | |
| throw new Error(result.error || 'Failed to fetch issues'); | |
| } | |
| return { | |
| openIssues: result.openIssues ?? [], | |
| closedIssues: result.closedIssues ?? [], | |
| }; | |
| }, | |
| enabled: !!projectPath, | |
| staleTime: STALE_TIMES.GITHUB, | |
| }); | |
| } | |
| /** | |
| * Fetch GitHub PRs for a project | |
| * | |
| * @param projectPath - Path to the project | |
| * @returns Query result with open and merged PRs | |
| */ | |
| export function useGitHubPRs(projectPath: string | undefined) { | |
| return useQuery({ | |
| queryKey: queryKeys.github.prs(projectPath ?? ''), | |
| queryFn: async (): Promise<GitHubPRsResult> => { | |
| if (!projectPath) throw new Error('No project path'); | |
| const api = getElectronAPI(); | |
| if (!api.github) { | |
| throw new Error('GitHub API not available'); | |
| } | |
| const result = await api.github.listPRs(projectPath); | |
| if (!result.success) { | |
| throw new Error(result.error || 'Failed to fetch PRs'); | |
| } | |
| return { | |
| openPRs: result.openPRs ?? [], | |
| mergedPRs: result.mergedPRs ?? [], | |
| }; | |
| }, | |
| enabled: !!projectPath, | |
| staleTime: STALE_TIMES.GITHUB, | |
| }); | |
| } | |
| /** | |
| * Fetch GitHub validations for a project | |
| * | |
| * @param projectPath - Path to the project | |
| * @param issueNumber - Optional issue number to filter by | |
| * @returns Query result with validations | |
| */ | |
| export function useGitHubValidations(projectPath: string | undefined, issueNumber?: number) { | |
| return useQuery({ | |
| queryKey: issueNumber | |
| ? queryKeys.github.validation(projectPath ?? '', issueNumber) | |
| : queryKeys.github.validations(projectPath ?? ''), | |
| queryFn: async (): Promise<StoredValidation[]> => { | |
| if (!projectPath) throw new Error('No project path'); | |
| const api = getElectronAPI(); | |
| if (!api.github) { | |
| throw new Error('GitHub API not available'); | |
| } | |
| const result = await api.github.getValidations(projectPath, issueNumber); | |
| if (!result.success) { | |
| throw new Error(result.error || 'Failed to fetch validations'); | |
| } | |
| return result.validations ?? []; | |
| }, | |
| enabled: !!projectPath, | |
| staleTime: STALE_TIMES.GITHUB, | |
| }); | |
| } | |
| /** | |
| * Check GitHub remote for a project | |
| * | |
| * @param projectPath - Path to the project | |
| * @returns Query result with remote info | |
| */ | |
| export function useGitHubRemote(projectPath: string | undefined) { | |
| return useQuery({ | |
| queryKey: queryKeys.github.remote(projectPath ?? ''), | |
| queryFn: async () => { | |
| if (!projectPath) throw new Error('No project path'); | |
| const api = getElectronAPI(); | |
| if (!api.github) { | |
| throw new Error('GitHub API not available'); | |
| } | |
| const result = await api.github.checkRemote(projectPath); | |
| if (!result.success) { | |
| throw new Error(result.error || 'Failed to check remote'); | |
| } | |
| return { | |
| hasRemote: result.hasGitHubRemote ?? false, | |
| owner: result.owner, | |
| repo: result.repo, | |
| url: result.remoteUrl, | |
| }; | |
| }, | |
| enabled: !!projectPath, | |
| staleTime: STALE_TIMES.GITHUB, | |
| }); | |
| } | |
| /** | |
| * Fetch comments for a GitHub issue with pagination support | |
| * | |
| * Uses useInfiniteQuery for proper "load more" pagination. | |
| * | |
| * @param projectPath - Path to the project | |
| * @param issueNumber - Issue number | |
| * @returns Infinite query result with comments and pagination helpers | |
| * | |
| * @example | |
| * ```tsx | |
| * const { | |
| * data, | |
| * isLoading, | |
| * isFetchingNextPage, | |
| * hasNextPage, | |
| * fetchNextPage, | |
| * refetch, | |
| * } = useGitHubIssueComments(projectPath, issueNumber); | |
| * | |
| * // Get all comments flattened | |
| * const comments = data?.pages.flatMap(page => page.comments) ?? []; | |
| * ``` | |
| */ | |
| export function useGitHubIssueComments( | |
| projectPath: string | undefined, | |
| issueNumber: number | undefined | |
| ) { | |
| return useInfiniteQuery({ | |
| queryKey: queryKeys.github.issueComments(projectPath ?? '', issueNumber ?? 0), | |
| queryFn: async ({ pageParam }: { pageParam: string | undefined }) => { | |
| if (!projectPath || !issueNumber) throw new Error('Missing project path or issue number'); | |
| const api = getElectronAPI(); | |
| if (!api.github) { | |
| throw new Error('GitHub API not available'); | |
| } | |
| const result = await api.github.getIssueComments(projectPath, issueNumber, pageParam); | |
| if (!result.success) { | |
| throw new Error(result.error || 'Failed to fetch comments'); | |
| } | |
| return { | |
| comments: (result.comments ?? []) as GitHubComment[], | |
| totalCount: result.totalCount ?? 0, | |
| hasNextPage: result.hasNextPage ?? false, | |
| endCursor: result.endCursor as string | undefined, | |
| }; | |
| }, | |
| initialPageParam: undefined as string | undefined, | |
| getNextPageParam: (lastPage) => (lastPage.hasNextPage ? lastPage.endCursor : undefined), | |
| enabled: !!projectPath && !!issueNumber, | |
| staleTime: STALE_TIMES.GITHUB, | |
| }); | |
| } | |
| /** | |
| * Fetch review comments for a GitHub PR | |
| * | |
| * Fetches both regular PR comments and inline code review comments | |
| * with file path and line context for each. | |
| * | |
| * @param projectPath - Path to the project | |
| * @param prNumber - PR number | |
| * @returns Query result with review comments | |
| * | |
| * @example | |
| * ```tsx | |
| * const { data, isLoading } = useGitHubPRReviewComments(projectPath, prNumber); | |
| * const comments = data?.comments ?? []; | |
| * ``` | |
| */ | |
| export function useGitHubPRReviewComments( | |
| projectPath: string | undefined, | |
| prNumber: number | undefined | |
| ) { | |
| return useQuery({ | |
| queryKey: queryKeys.github.prReviewComments(projectPath ?? '', prNumber ?? 0), | |
| queryFn: async (): Promise<{ comments: PRReviewComment[]; totalCount: number }> => { | |
| if (!projectPath || !prNumber) throw new Error('Missing project path or PR number'); | |
| const api = getElectronAPI(); | |
| if (!api.github) { | |
| throw new Error('GitHub API not available'); | |
| } | |
| const result = await api.github.getPRReviewComments(projectPath, prNumber); | |
| if (!result.success) { | |
| throw new Error(result.error || 'Failed to fetch PR review comments'); | |
| } | |
| return { | |
| comments: (result.comments ?? []) as PRReviewComment[], | |
| totalCount: result.totalCount ?? 0, | |
| }; | |
| }, | |
| enabled: !!projectPath && !!prNumber, | |
| staleTime: STALE_TIMES.GITHUB, | |
| }); | |
| } | |