gitdataai/src/contexts/repository-context.tsx
ZhenYi 99bc4eeb80 chore: API and frontend UI adjustments
- API: issue label bulk add, search messages, room WS push, openapi
- Frontend: notify page, issue detail AI triage banner, search page,
  repository settings, preferences, PR components, file browser
- Room: DiscordChannelSidebar, RoomPinPanel, RoomMessageActions,
  RoomThreadPanel, MessageContent, repository-context
- Frontend SDK regenerated from openapi.json
2026-04-25 09:54:05 +08:00

124 lines
3.8 KiB
TypeScript

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<RepoInfo | null>(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 (
<ProjectProvider projectName={namespace}>
<RepositoryContext.Provider value={repo}>{children}</RepositoryContext.Provider>
</ProjectProvider>
);
};
export function useRepo(): RepoInfo {
const ctx = useContext(RepositoryContext);
if (!ctx) throw new Error("useRepo must be used within RepositoryContextProvider");
return ctx;
}