- 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
124 lines
3.8 KiB
TypeScript
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;
|
|
}
|