2026-03-06 17:22:59 +02:00
|
|
|
import React from 'react';
|
|
|
|
|
import { getRootBranch } from '@/lib/worktrees/worktreeStatus';
|
2026-04-03 19:47:01 +03:00
|
|
|
import { mapWithConcurrency } from '@/lib/concurrency';
|
|
|
|
|
import { useGitStore } from '@/stores/useGitStore';
|
|
|
|
|
import { useRuntimeAPIs } from '@/hooks/useRuntimeAPIs';
|
2026-03-06 17:22:59 +02:00
|
|
|
|
|
|
|
|
type Project = { id: string; path: string; normalizedPath: string };
|
|
|
|
|
|
|
|
|
|
type Args = {
|
|
|
|
|
normalizedProjects: Project[];
|
2026-04-03 19:47:01 +03:00
|
|
|
gitRepoStatus: Map<string, { isGitRepo: boolean | null; branch: string | null }>;
|
2026-03-06 17:22:59 +02:00
|
|
|
setProjectRepoStatus: React.Dispatch<React.SetStateAction<Map<string, boolean | null>>>;
|
|
|
|
|
setProjectRootBranches: React.Dispatch<React.SetStateAction<Map<string, string>>>;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const useProjectRepoStatus = (args: Args): void => {
|
|
|
|
|
const {
|
|
|
|
|
normalizedProjects,
|
2026-04-03 19:47:01 +03:00
|
|
|
gitRepoStatus,
|
2026-03-06 17:22:59 +02:00
|
|
|
setProjectRepoStatus,
|
|
|
|
|
setProjectRootBranches,
|
|
|
|
|
} = args;
|
|
|
|
|
|
2026-04-03 19:47:01 +03:00
|
|
|
const { git } = useRuntimeAPIs();
|
|
|
|
|
const ensureStatus = useGitStore((state) => state.ensureStatus);
|
2026-03-06 17:22:59 +02:00
|
|
|
|
2026-04-03 19:47:01 +03:00
|
|
|
// Derive repo status from centralized Git store
|
|
|
|
|
React.useEffect(() => {
|
|
|
|
|
if (!git || normalizedProjects.length === 0) {
|
|
|
|
|
setProjectRepoStatus(new Map());
|
|
|
|
|
return;
|
2026-03-06 17:22:59 +02:00
|
|
|
}
|
|
|
|
|
|
2026-04-03 19:47:01 +03:00
|
|
|
// Trigger ensureStatus for each project to populate store
|
|
|
|
|
normalizedProjects.forEach((project) => {
|
|
|
|
|
void ensureStatus(project.normalizedPath, git);
|
2026-03-06 17:22:59 +02:00
|
|
|
});
|
2026-04-03 19:47:01 +03:00
|
|
|
}, [normalizedProjects, git, ensureStatus, setProjectRepoStatus]);
|
2026-03-06 17:22:59 +02:00
|
|
|
|
2026-04-03 19:47:01 +03:00
|
|
|
// Read isGitRepo from the store-populated state
|
|
|
|
|
React.useEffect(() => {
|
|
|
|
|
const next = new Map<string, boolean | null>();
|
|
|
|
|
normalizedProjects.forEach((project) => {
|
|
|
|
|
next.set(project.id, gitRepoStatus.get(project.normalizedPath)?.isGitRepo ?? null);
|
|
|
|
|
});
|
|
|
|
|
setProjectRepoStatus(next);
|
|
|
|
|
}, [normalizedProjects, gitRepoStatus, setProjectRepoStatus]);
|
2026-03-06 17:22:59 +02:00
|
|
|
|
|
|
|
|
const projectGitBranchesKey = React.useMemo(() => {
|
|
|
|
|
return normalizedProjects
|
|
|
|
|
.map((project) => {
|
2026-04-03 19:47:01 +03:00
|
|
|
const branch = gitRepoStatus.get(project.normalizedPath)?.branch ?? '';
|
|
|
|
|
return `${project.id}:${branch}`;
|
2026-03-06 17:22:59 +02:00
|
|
|
})
|
|
|
|
|
.join('|');
|
2026-04-03 19:47:01 +03:00
|
|
|
}, [normalizedProjects, gitRepoStatus]);
|
2026-03-06 17:22:59 +02:00
|
|
|
|
|
|
|
|
React.useEffect(() => {
|
|
|
|
|
let cancelled = false;
|
|
|
|
|
const run = async () => {
|
2026-03-31 18:47:00 +03:00
|
|
|
const entries = await mapWithConcurrency(normalizedProjects, 2, async (project) => {
|
|
|
|
|
const branch = await getRootBranch(project.normalizedPath).catch(() => null);
|
|
|
|
|
return { id: project.id, branch };
|
|
|
|
|
});
|
2026-03-06 17:22:59 +02:00
|
|
|
if (cancelled) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
setProjectRootBranches((prev) => {
|
|
|
|
|
const next = new Map(prev);
|
|
|
|
|
entries.forEach(({ id, branch }) => {
|
|
|
|
|
if (branch) {
|
|
|
|
|
next.set(id, branch);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
return next;
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
void run();
|
|
|
|
|
return () => {
|
|
|
|
|
cancelled = true;
|
|
|
|
|
};
|
|
|
|
|
}, [normalizedProjects, projectGitBranchesKey, setProjectRootBranches]);
|
|
|
|
|
};
|