feat(git): resolve nested repositories in the other git surfaces
Extract GitView's resolution flow into hooks/useNestedGitDirectory (root probe, discovery, auto-select, stale-selection recovery) so the flow no longer depends on SessionSidebar probing the root first, and reuse it in the pull-request view, walkthrough view, and mobile changes surface — all three now operate on the selected nested repository instead of dead-ending on a non-repo root. Shared pending/failed/unsupported/empty states live in git/NestedRepoResolutionStates; desktop changes inherits the behavior through GitView. Selection stays shared per root, so the picker's pick carries across surfaces.
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
import React from 'react';
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
|
||||
import { useRuntimeAPIs } from '@/hooks/useRuntimeAPIs';
|
||||
import {
|
||||
useEffectiveGitDirectory,
|
||||
useGitStore,
|
||||
useIsGitRepo,
|
||||
useNestedRepoSelection,
|
||||
useNestedRepos,
|
||||
} from '@/stores/useGitStore';
|
||||
|
||||
type UseNestedGitDirectoryOptions = {
|
||||
/** False defers all probing/discovery work while the surface is hidden. */
|
||||
enabled?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolves the repository a git surface operates on when the project root may
|
||||
* not itself be a git repository. Owns the full resolution flow: probing the
|
||||
* root, discovering nested repositories, auto-selecting the first one, and
|
||||
* dropping a selection whose repository disappeared.
|
||||
*
|
||||
* Consumers still fetch their own git data for the returned `gitDirectory`;
|
||||
* this hook only owns who that directory is.
|
||||
*/
|
||||
export const useNestedGitDirectory = (
|
||||
root: string | null,
|
||||
options: UseNestedGitDirectoryOptions = {},
|
||||
) => {
|
||||
const { enabled = true } = options;
|
||||
const { git } = useRuntimeAPIs();
|
||||
|
||||
const rootIsGitRepo = useIsGitRepo(root);
|
||||
const gitDirectory = useEffectiveGitDirectory(root);
|
||||
const nestedRepos = useNestedRepos(root);
|
||||
const nestedRepoSelection = useNestedRepoSelection(root);
|
||||
|
||||
// Probe of the resolved repository, used to detect a stale selection. Null
|
||||
// when there is nothing selected to probe.
|
||||
const selectedIsGitRepo = useIsGitRepo(
|
||||
gitDirectory && gitDirectory !== root ? gitDirectory : null,
|
||||
);
|
||||
|
||||
const { ensureStatus, ensureNestedRepos, selectNestedRepo, clearNestedRepoSelection } = useGitStore(
|
||||
useShallow((state) => ({
|
||||
ensureStatus: state.ensureStatus,
|
||||
ensureNestedRepos: state.ensureNestedRepos,
|
||||
selectNestedRepo: state.selectNestedRepo,
|
||||
clearNestedRepoSelection: state.clearNestedRepoSelection,
|
||||
})),
|
||||
);
|
||||
|
||||
// Probe the root itself so nested-repo resolution never depends on some
|
||||
// other surface (e.g. the sidebar badge) having probed it first.
|
||||
React.useEffect(() => {
|
||||
if (!enabled || !root) return;
|
||||
if (rootIsGitRepo !== null) return;
|
||||
void ensureStatus(root, git);
|
||||
}, [enabled, ensureStatus, git, root, rootIsGitRepo]);
|
||||
|
||||
// Discover nested repositories once the root probe confirms it is not one.
|
||||
React.useEffect(() => {
|
||||
if (!enabled || !root) return;
|
||||
if (rootIsGitRepo !== false) return;
|
||||
void ensureNestedRepos(root);
|
||||
}, [enabled, ensureNestedRepos, root, rootIsGitRepo]);
|
||||
|
||||
// Auto-select the first nested repository so the surface opens straight
|
||||
// into repository data; a picker (where rendered) switches between them.
|
||||
React.useEffect(() => {
|
||||
if (!enabled || !root) return;
|
||||
if (rootIsGitRepo !== false) return;
|
||||
if (!nestedRepos || nestedRepos.length === 0) return;
|
||||
if (nestedRepoSelection) return;
|
||||
selectNestedRepo(root, nestedRepos[0]);
|
||||
}, [enabled, nestedRepos, nestedRepoSelection, root, rootIsGitRepo, selectNestedRepo]);
|
||||
|
||||
// A selected repository that is no longer a git repository is stale: drop
|
||||
// the selection and re-scan so resolution reflects the current tree.
|
||||
React.useEffect(() => {
|
||||
if (!enabled || !root || !nestedRepoSelection) return;
|
||||
if (!gitDirectory || gitDirectory === root) return;
|
||||
if (selectedIsGitRepo !== false) return;
|
||||
clearNestedRepoSelection(root);
|
||||
void ensureNestedRepos(root, { force: true });
|
||||
}, [
|
||||
clearNestedRepoSelection,
|
||||
enabled,
|
||||
ensureNestedRepos,
|
||||
gitDirectory,
|
||||
nestedRepoSelection,
|
||||
root,
|
||||
selectedIsGitRepo,
|
||||
]);
|
||||
|
||||
return { rootIsGitRepo, gitDirectory, nestedRepos, nestedRepoSelection };
|
||||
};
|
||||
Reference in New Issue
Block a user