feat: add build in progress modal when trying to exit (#3695)

* Changed store to stop the isBuilding state as well

* Create Build In Progress modal

* Use blocker to stop if its building as well
This commit is contained in:
Lucas Oliveira 2024-09-05 10:50:12 -03:00 • committed by GitHub
commit a4dc5381b2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 80 additions and 23 deletions

View file

@ -0,0 +1,26 @@
import ConfirmationModal from "../confirmationModal";
export function BuildInProgressModal({
onStopBuild,
onCancel,
}: {
onStopBuild: () => void;
onCancel: () => void;
}): JSX.Element {
return (
<ConfirmationModal
open={true}
onClose={onCancel}
title="Build in Progress"
cancelText="Cancel"
confirmationText="Stop Build"
onConfirm={onStopBuild}
onCancel={onCancel}
size="x-small"
>
<ConfirmationModal.Content>
The flow is currently building. Do you want to stop the build and exit?
</ConfirmationModal.Content>
</ConfirmationModal>
);
}

View file

@ -8,6 +8,7 @@ import { customStringify } from "@/utils/reactflowUtils";
import { useEffect } from "react"; import { useEffect } from "react";
import { useBlocker, useParams } from "react-router-dom"; import { useBlocker, useParams } from "react-router-dom";
import FlowToolbar from "../../components/chatComponent"; import FlowToolbar from "../../components/chatComponent";
import { BuildInProgressModal } from "../../modals/buildInProgressModal";
import { useDarkStore } from "../../stores/darkStore"; import { useDarkStore } from "../../stores/darkStore";
import useFlowStore from "../../stores/flowStore"; import useFlowStore from "../../stores/flowStore";
import useFlowsManagerStore from "../../stores/flowsManagerStore"; import useFlowsManagerStore from "../../stores/flowsManagerStore";
@ -23,7 +24,9 @@ export default function FlowPage({ view }: { view?: boolean }): JSX.Element {
customStringify(currentFlow) !== customStringify(currentSavedFlow) && customStringify(currentFlow) !== customStringify(currentSavedFlow) &&
(currentFlow?.data?.nodes?.length ?? 0) > 0; (currentFlow?.data?.nodes?.length ?? 0) > 0;
const blocker = useBlocker(changesNotSaved); const isBuilding = useFlowStore((state) => state.isBuilding);
const blocker = useBlocker(changesNotSaved || isBuilding);
const version = useDarkStore((state) => state.version); const version = useDarkStore((state) => state.version);
const setOnFlowPage = useFlowStore((state) => state.setOnFlowPage); const setOnFlowPage = useFlowStore((state) => state.setOnFlowPage);
const { id } = useParams(); const { id } = useParams();
@ -41,13 +44,30 @@ export default function FlowPage({ view }: { view?: boolean }): JSX.Element {
const autoSaving = useFlowsManagerStore((state) => state.autoSaving); const autoSaving = useFlowsManagerStore((state) => state.autoSaving);
const stopBuilding = useFlowStore((state) => state.stopBuilding);
const handleSave = () => { const handleSave = () => {
saveFlow().then(() => (blocker.proceed ? blocker.proceed() : null)); saveFlow().then(() => (blocker.proceed ? blocker.proceed() : null));
}; };
const handleStopBuild = () => {
stopBuilding();
if (blocker.proceed) blocker.proceed();
};
const handleExit = () => {
if (isBuilding) {
// Do nothing, let the blocker handle it
} else if (changesNotSaved) {
if (blocker.proceed) blocker.proceed();
} else {
navigate("/all");
}
};
useEffect(() => { useEffect(() => {
const handleBeforeUnload = (event: BeforeUnloadEvent) => { const handleBeforeUnload = (event: BeforeUnloadEvent) => {
if (changesNotSaved) { if (changesNotSaved || isBuilding) {
event.preventDefault(); event.preventDefault();
event.returnValue = ""; // Required for Chrome event.returnValue = ""; // Required for Chrome
} }
@ -58,7 +78,7 @@ export default function FlowPage({ view }: { view?: boolean }): JSX.Element {
return () => { return () => {
window.removeEventListener("beforeunload", handleBeforeUnload); window.removeEventListener("beforeunload", handleBeforeUnload);
}; };
}, [changesNotSaved, navigate]); }, [changesNotSaved, isBuilding]);
// Set flow tab id // Set flow tab id
useEffect(() => { useEffect(() => {
@ -118,11 +138,19 @@ export default function FlowPage({ view }: { view?: boolean }): JSX.Element {
</a> </a>
)} )}
</div> </div>
{blocker.state === "blocked" && currentSavedFlow && ( {blocker.state === "blocked" && (
<>
{isBuilding && (
<BuildInProgressModal
onStopBuild={handleStopBuild}
onCancel={() => blocker.reset?.()}
/>
)}
{!isBuilding && currentSavedFlow && (
<SaveChangesModal <SaveChangesModal
onSave={handleSave} onSave={handleSave}
onCancel={() => (blocker.reset ? blocker.reset() : null)} onCancel={() => blocker.reset?.()}
onProceed={() => (blocker.proceed ? blocker.proceed() : null)} onProceed={handleExit}
flowName={currentSavedFlow.name} flowName={currentSavedFlow.name}
unsavedChanges={changesNotSaved} unsavedChanges={changesNotSaved}
lastSaved={ lastSaved={
@ -140,5 +168,7 @@ export default function FlowPage({ view }: { view?: boolean }): JSX.Element {
/> />
)} )}
</> </>
)}
</>
); );
} }

View file

@ -81,6 +81,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
isBuilding: false, isBuilding: false,
stopBuilding: () => { stopBuilding: () => {
get().buildController.abort(); get().buildController.abort();
set({ isBuilding: false });
}, },
isPending: true, isPending: true,
setHasIO: (hasIO) => { setHasIO: (hasIO) => {