import React, { useContext, useMemo } from "react"; import { useQuery } from "@tanstack/react-query"; import { gitIsStarred, gitIsWatched, projectRepos } from "@/client"; import { ProjectProvider } from "@/contexts"; import type { ProjectRepositoryItem } from "@/client"; /** Enriched repo info combining ProjectRepositoryItem with star/watch status */ export interface RepoInfo { /** Project namespace (= project_name) */ namespace: string; /** Repository name */ repo_name: string; /** Repository uid */ uid: string; description?: string | null; default_branch: string; is_private: boolean; commit_count: number; branch_count: number; tag_count: number; star_count: number; watch_count: number; last_commit_at?: string | null; /** Whether the current user has starred this repo */ is_star: boolean; /** Whether the current user is watching this repo */ is_watch: boolean; /** SSH clone URL */ ssh_clone_url: string; /** HTTPS clone URL */ https_clone_url: string; /** Whether AI auto-review is enabled for this repo */ ai_code_review_enabled: boolean; } export const RepositoryContext = React.createContext(null); export const RepositoryContextProvider = ({ children, namespace, repoName, }: { children: React.ReactNode; namespace: string; repoName: string; }) => { // Fetch the repo list for this project to get repo metadata const { data: reposData, isLoading: reposLoading } = useQuery({ queryKey: ["projectRepos", namespace], queryFn: async () => { const resp = await projectRepos({ path: { project_name: namespace } }); return resp.data?.data ?? null; }, staleTime: 5 * 60 * 1000, enabled: !!namespace, }); const repoItem = useMemo( () => reposData?.items?.find((r: ProjectRepositoryItem) => r.repo_name === repoName) ?? null, [reposData, repoName], ) as ProjectRepositoryItem | null; // Fetch star status const { data: starCountResp } = useQuery({ queryKey: ["repoStar", namespace, repoName], queryFn: async () => { const resp = await gitIsStarred({ path: { namespace, repo: repoName } }); return resp.data?.data; }, staleTime: 30 * 1000, retry: false, enabled: !!namespace && !!repoName, }); // Fetch watch status const { data: watchCountResp } = useQuery({ queryKey: ["repoWatch", namespace, repoName], queryFn: async () => { const resp = await gitIsWatched({ path: { namespace, repo: repoName } }); return resp.data?.data; }, staleTime: 30 * 1000, retry: false, enabled: !!namespace && !!repoName, }); const repo: RepoInfo | null = useMemo(() => { if (!repoItem) return null; return { namespace, repo_name: repoItem.repo_name, uid: repoItem.uid, description: repoItem.description, default_branch: repoItem.default_branch, is_private: repoItem.is_private, commit_count: repoItem.commit_count, branch_count: repoItem.branch_count, tag_count: repoItem.tag_count, star_count: repoItem.star_count, watch_count: repoItem.watch_count, last_commit_at: repoItem.last_commit_at, is_star: Boolean(starCountResp), is_watch: Boolean(watchCountResp), ssh_clone_url: repoItem.ssh_clone_url, https_clone_url: repoItem.https_clone_url, ai_code_review_enabled: repoItem.ai_code_review_enabled, }; }, [repoItem, namespace, starCountResp, watchCountResp]); if (reposLoading) return null; return ( {children} ); }; export function useRepo(): RepoInfo { const ctx = useContext(RepositoryContext); if (!ctx) throw new Error("useRepo must be used within RepositoryContextProvider"); return ctx; }