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,26 +138,36 @@ export default function FlowPage({ view }: { view?: boolean }): JSX.Element {
</a> </a>
)} )}
</div> </div>
{blocker.state === "blocked" && currentSavedFlow && ( {blocker.state === "blocked" && (
<SaveChangesModal <>
onSave={handleSave} {isBuilding && (
onCancel={() => (blocker.reset ? blocker.reset() : null)} <BuildInProgressModal
onProceed={() => (blocker.proceed ? blocker.proceed() : null)} onStopBuild={handleStopBuild}
flowName={currentSavedFlow.name} onCancel={() => blocker.reset?.()}
unsavedChanges={changesNotSaved} />
lastSaved={ )}
updatedAt {!isBuilding && currentSavedFlow && (
? new Date(updatedAt).toLocaleString("en-US", { <SaveChangesModal
hour: "numeric", onSave={handleSave}
minute: "numeric", onCancel={() => blocker.reset?.()}
second: "numeric", onProceed={handleExit}
month: "numeric", flowName={currentSavedFlow.name}
day: "numeric", unsavedChanges={changesNotSaved}
}) lastSaved={
: undefined updatedAt
} ? new Date(updatedAt).toLocaleString("en-US", {
autoSave={autoSaving} hour: "numeric",
/> minute: "numeric",
second: "numeric",
month: "numeric",
day: "numeric",
})
: undefined
}
autoSave={autoSaving}
/>
)}
</>
)} )}
</> </>
); );

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) => {