Move GitProjectStagingModal to global [INS-1988] (#9705)

* Move GitProjectStagingModal to global

* Fix codereview issues
This commit is contained in:
yaoweiprc
2026-03-14 18:27:21 +08:00
committed by GitHub
parent 723baafd17
commit e3f3c19777
3 changed files with 119 additions and 40 deletions

View File

@@ -37,7 +37,11 @@ import { showModal } from '../modals';
import { GitProjectBranchesModal } from '../modals/git-project-branches-modal';
import { GitProjectLogModal } from '../modals/git-project-log-modal';
import { GitProjectMigrationModal } from '../modals/git-project-migration-modal';
import { GitProjectStagingModal, type StagingModalMode, StagingModalModes } from '../modals/git-project-staging-modal';
import {
GitProjectStagingModal,
type GitProjectStagingModalCallbackProps,
StagingModalModes,
} from '../modals/git-project-staging-modal';
import { GitPullRequiredModal } from '../modals/git-pull-required-modal';
import { SyncMergeModal } from '../modals/sync-merge-modal';
import { showToast } from '../toast-notification';
@@ -54,9 +58,7 @@ export const GitProjectSyncDropdown: FC<Props> = ({ gitRepository, activeProject
const [isGitBranchesModalOpen, setIsGitBranchesModalOpen] = useState(false);
const [isGitLogModalOpen, setIsGitLogModalOpen] = useState(false);
const [isGitStagingModalOpen, setIsGitStagingModalOpen] = useState(false);
const [isGitPullRequiredModalOpen, setIsGitPullRequiredModalOpen] = useState(false);
const [stagingMode, setStagingMode] = useState<StagingModalMode>(StagingModalModes.default);
const [isMigrationModalOpen, setIsMigrationModalOpen] = useState(false);
const prevHadPullError = useRef(false);
@@ -257,6 +259,29 @@ export const GitProjectSyncDropdown: FC<Props> = ({ gitRepository, activeProject
? gitRepoDataFetcher.data
: { branches: [], branch: '' };
const closeGitProjectStagingModalRef = useRef<(() => void) | null>(null);
const gitProjectStagingModalCallbackPropsRef = useRef<GitProjectStagingModalCallbackProps>(null!);
gitProjectStagingModalCallbackPropsRef.current = {
onPullAfterCommit: async () => {
await handlePull();
fetchStatus();
},
onPushAfterPull: async () => {
setIsGitPullRequiredModalOpen(false);
const pullResult = await handlePull();
if (pullResult && pullResult.success) {
handlePush({ force: false });
}
prevHadPullError.current = true;
fetchStatus();
},
onClose: () => {
prevHadPullError.current = false;
fetchStatus();
},
};
const handlePull = async () => {
try {
setIsPulling(true);
@@ -274,8 +299,10 @@ export const GitProjectSyncDropdown: FC<Props> = ({ gitRepository, activeProject
pullResult.errors.includes(GitVCSOperationErrors.UncommittedChangesError)
) {
setIsPulling(false);
setStagingMode(StagingModalModes.commitAndPull);
setIsGitStagingModalOpen(true);
closeGitProjectStagingModalRef.current = showModal(GitProjectStagingModal, {
mode: StagingModalModes.commitAndPull,
callbackRef: gitProjectStagingModalCallbackPropsRef,
});
} else if ('errors' in pullResult && pullResult.errors) {
showToast({
icon,
@@ -333,7 +360,7 @@ export const GitProjectSyncDropdown: FC<Props> = ({ gitRepository, activeProject
});
},
onCancelUnresolved: () => {
setIsGitStagingModalOpen(false);
closeGitProjectStagingModalRef.current?.();
setIsPulling(false);
showToast({
icon,
@@ -394,7 +421,12 @@ export const GitProjectSyncDropdown: FC<Props> = ({ gitRepository, activeProject
icon: 'check',
isDisabled: status?.localChanges === 0,
label: 'Commit',
action: () => setIsGitStagingModalOpen(true),
action: () => {
closeGitProjectStagingModalRef.current = showModal(GitProjectStagingModal, {
mode: StagingModalModes.default,
callbackRef: gitProjectStagingModalCallbackPropsRef,
});
},
},
{
id: 'pull',
@@ -658,36 +690,7 @@ export const GitProjectSyncDropdown: FC<Props> = ({ gitRepository, activeProject
/>
)}
{isGitLogModalOpen && gitRepository && <GitProjectLogModal onClose={() => setIsGitLogModalOpen(false)} />}
{isGitStagingModalOpen && gitRepository && (
<GitProjectStagingModal
mode={stagingMode}
onPullAfterCommit={async () => {
setIsGitStagingModalOpen(false);
setStagingMode(StagingModalModes.default);
await handlePull();
fetchStatus();
}}
onPushAfterPull={async () => {
setIsGitPullRequiredModalOpen(false);
const pullResult = await handlePull();
if (pullResult && pullResult.success) {
handlePush({ force: false });
}
prevHadPullError.current = true;
fetchStatus();
}}
onClose={() => {
prevHadPullError.current = false;
setIsGitStagingModalOpen(false);
setStagingMode(StagingModalModes.default);
fetchStatus();
}}
/>
)}
{isMigrationModalOpen && gitRepository && legacyInsomniaWorkspace && (
<GitProjectMigrationModal
legacyFile={legacyInsomniaWorkspace}

View File

@@ -1,4 +1,13 @@
import React, { type FC, type ReactNode, useCallback, useEffect, useRef, useState } from 'react';
import React, {
type FC,
forwardRef,
type ReactNode,
useCallback,
useEffect,
useImperativeHandle,
useRef,
useState,
} from 'react';
import {
Button,
Dialog,
@@ -901,12 +910,76 @@ const ManualCommitForm: FC<ManualCommitFormProps> = ({
);
};
export const GitProjectStagingModal: FC<{
mode?: StagingModalMode;
export interface GitProjectStagingModalCallbackProps {
onClose: () => void;
onPullAfterCommit: () => void;
onPushAfterPull: () => void;
}> = ({ mode = StagingModalModes.default, onClose, onPullAfterCommit, onPushAfterPull }) => {
}
export interface GitProjectStagingModalOptions {
mode?: StagingModalMode;
/* Why is callbackRef a ref object?
* The callbacks passed to the modal (onClose, onPullAfterCommit, onPushAfterPull) may change after the show function is called.
* If we were to pass the callbacks directly, the modal would capture the initial callbacks and not reflect any updates to them.
* By using a ref object, we can ensure that the modal always has access to the latest version of the callbacks, even if they change after the modal is shown.
*/
callbackRef: React.MutableRefObject<GitProjectStagingModalCallbackProps>;
}
export interface GitProjectStagingModalHandle {
show: (options: GitProjectStagingModalOptions) => void;
hide: () => void;
}
export const GitProjectStagingModal = forwardRef<GitProjectStagingModalHandle>((_, ref) => {
const [isOpen, setIsOpen] = useState(false);
const [modalOptions, setModalOptions] = useState<GitProjectStagingModalOptions | null>(null);
const hide = useCallback(() => {
setIsOpen(false);
setModalOptions(null);
}, []);
useImperativeHandle(ref, () => ({
show: ({ mode: newMode = StagingModalModes.default, callbackRef }) => {
setModalOptions({ mode: newMode, callbackRef });
setIsOpen(true);
},
hide,
}));
const onClose = useCallback(() => {
modalOptions?.callbackRef.current.onClose();
hide();
}, [hide, modalOptions]);
const onPullAfterCommit = useCallback(() => {
modalOptions?.callbackRef.current.onPullAfterCommit();
hide();
}, [hide, modalOptions]);
const onPushAfterPull = useCallback(() => {
modalOptions?.callbackRef.current.onPushAfterPull();
}, [modalOptions]);
return (
isOpen && (
<OriginalGitProjectStagingModal
mode={modalOptions?.mode}
onClose={onClose}
onPullAfterCommit={onPullAfterCommit}
onPushAfterPull={onPushAfterPull}
/>
)
);
});
GitProjectStagingModal.displayName = 'GitProjectStagingModal';
const OriginalGitProjectStagingModal: FC<
{
mode?: StagingModalMode;
} & GitProjectStagingModalCallbackProps
> = ({ mode = StagingModalModes.default, onClose, onPullAfterCommit, onPushAfterPull }) => {
const { projectId } = useParams() as { projectId: string };
const [commitGenerationKey, setCommitGenerationKey] = useState(0);

View File

@@ -10,6 +10,7 @@ import { AskModal } from './components/modals/ask-modal';
import { CodePromptModal } from './components/modals/code-prompt-modal';
import { ErrorModal } from './components/modals/error-modal';
import { GenerateCodeModal } from './components/modals/generate-code-modal';
import { GitProjectStagingModal } from './components/modals/git-project-staging-modal';
import { LogoutModal } from './components/modals/logout-modal';
import { NunjucksModal } from './components/modals/nunjucks-modal';
import { PromptModal } from './components/modals/prompt-modal';
@@ -58,6 +59,8 @@ const Modals = () => {
<SyncMergeModal ref={instance => registerModal(instance, 'SyncMergeModal')} />
<GitProjectStagingModal ref={instance => registerModal(instance, 'GitProjectStagingModal')} />
<UpgradeModal ref={instance => registerModal(instance, 'UpgradeModal')} />
<LogoutModal ref={instance => registerModal(instance, 'LogoutModal')} />