refactor: simplify modal mounting with lazy wrapper and add read-only git env

This commit is contained in:
Mohamed Boudra
2025-12-03 14:09:56 +07:00
parent f4f7790d07
commit d5f8091d66
5 changed files with 51 additions and 24 deletions
+6 -17
View File
@@ -24,8 +24,6 @@ export default function HomeScreen() {
} = useAggregatedAgents();
const [showCreateModal, setShowCreateModal] = useState(false);
const [showImportModal, setShowImportModal] = useState(false);
const [createModalMounted, setCreateModalMounted] = useState(false);
const [importModalMounted, setImportModalMounted] = useState(false);
const [pendingImportServerId, setPendingImportServerId] = useState<string | null>(null);
const { modal, flow, action, serverId: serverIdParam } = useLocalSearchParams<{
modal?: string;
@@ -49,13 +47,11 @@ export default function HomeScreen() {
const hasAgents = aggregatedAgents.length > 0;
const handleCreateAgent = useCallback(() => {
setCreateModalMounted(true);
setShowCreateModal(true);
}, []);
const openImportModal = useCallback((serverIdOverride?: string | null) => {
setPendingImportServerId(serverIdOverride ?? null);
setImportModalMounted(true);
setShowImportModal(true);
}, []);
@@ -138,19 +134,12 @@ export default function HomeScreen() {
</ReanimatedAnimated.View>
{/* Create Agent Modal */}
{createModalMounted ? (
<CreateAgentModal
isVisible={showCreateModal}
onClose={handleCloseCreateModal}
/>
) : null}
{importModalMounted ? (
<ImportAgentModal
isVisible={showImportModal}
onClose={handleCloseImportModal}
serverId={pendingImportServerId}
/>
) : null}
<CreateAgentModal isVisible={showCreateModal} onClose={handleCloseCreateModal} />
<ImportAgentModal
isVisible={showImportModal}
onClose={handleCloseImportModal}
serverId={pendingImportServerId}
/>
</View>
);
}
@@ -82,6 +82,7 @@ interface AgentFlowModalProps {
flow: "create" | "import";
initialValues?: CreateAgentInitialValues;
serverId?: string | null;
onAfterClose?: () => void;
}
type DictationToastConfig = {
@@ -237,6 +238,7 @@ function AgentFlowModal({
flow,
initialValues,
serverId,
onAfterClose,
}: AgentFlowModalProps) {
const insets = useSafeAreaInsets();
const { height: screenHeight, width: screenWidth } = useWindowDimensions();
@@ -1326,7 +1328,8 @@ function AgentFlowModal({
resetFormState();
setIsMounted(false);
navigateToAgentIfNeeded();
}, [navigateToAgentIfNeeded, resetFormState]);
onAfterClose?.();
}, [navigateToAgentIfNeeded, onAfterClose, resetFormState]);
const requestImportCandidates = useCallback(
(provider?: AgentProvider) => {
@@ -2433,12 +2436,33 @@ function AgentFlowModal({
);
}
function LazyAgentFlowModal(props: Omit<AgentFlowModalProps, "onAfterClose">) {
const { isVisible } = props;
const [shouldRender, setShouldRender] = useState(isVisible);
useEffect(() => {
if (isVisible) {
setShouldRender(true);
}
}, [isVisible]);
const handleAfterClose = useCallback(() => {
setShouldRender(false);
}, []);
if (!shouldRender) {
return null;
}
return <AgentFlowModal {...props} onAfterClose={handleAfterClose} />;
}
export function CreateAgentModal(props: ModalWrapperProps) {
return <AgentFlowModal {...props} flow="create" />;
return <LazyAgentFlowModal {...props} flow="create" />;
}
export function ImportAgentModal(props: ModalWrapperProps) {
return <AgentFlowModal {...props} flow="import" />;
return <LazyAgentFlowModal {...props} flow="import" />;
}
interface ModalHeaderProps {
@@ -171,7 +171,9 @@ export function GlobalFooter() {
</Pressable>
<Pressable
onPress={() => setShowImportModal(true)}
onPress={() => {
setShowImportModal(true);
}}
style={({ pressed }) => [
styles.footerButton,
pressed && styles.buttonPressed,
+10 -3
View File
@@ -72,6 +72,10 @@ type AgentMcpClientConfig = {
};
const execAsync = promisify(exec);
const READ_ONLY_GIT_ENV: NodeJS.ProcessEnv = {
...process.env,
GIT_OPTIONAL_LOCKS: "0",
};
const ACTIVE_TITLE_GENERATIONS = new Set<string>();
const pendingAgentInitializations = new Map<string, Promise<ManagedAgent>>();
let restartRequested = false;
@@ -1526,11 +1530,11 @@ export class Session {
const repoInfo = await detectRepoInfo(resolvedCwd);
const { stdout: branchesRaw } = await execAsync(
"git branch --format='%(refname:short)'",
{ cwd: repoInfo.path }
{ cwd: repoInfo.path, env: READ_ONLY_GIT_ENV }
);
const { stdout: currentRaw } = await execAsync(
"git rev-parse --abbrev-ref HEAD",
{ cwd: resolvedCwd }
{ cwd: resolvedCwd, env: READ_ONLY_GIT_ENV }
);
const currentBranch = currentRaw.trim();
const branches = branchesRaw
@@ -1687,7 +1691,10 @@ export class Session {
private async isWorkingTreeDirty(cwd: string): Promise<boolean> {
try {
const { stdout } = await execAsync("git status --porcelain", { cwd });
const { stdout } = await execAsync("git status --porcelain", {
cwd,
env: READ_ONLY_GIT_ENV,
});
return stdout.trim().length > 0;
} catch (error) {
throw new Error(
+5
View File
@@ -4,6 +4,10 @@ import { existsSync, readFileSync } from "fs";
import { join, basename, dirname } from "path";
const execAsync = promisify(exec);
const READ_ONLY_GIT_ENV: NodeJS.ProcessEnv = {
...process.env,
GIT_OPTIONAL_LOCKS: "0",
};
interface RepoInfo {
type: "bare" | "normal";
@@ -88,6 +92,7 @@ export async function detectRepoInfo(cwd: string): Promise<RepoInfo> {
try {
const { stdout } = await execAsync("git rev-parse --show-toplevel", {
cwd,
env: READ_ONLY_GIT_ENV,
});
const repoRoot = stdout.trim();
return {