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