fix: restored save on change, simplified tool_mode attribution to make it work between updates (#5599)
* Fixed flowStore to run autoSave on setNode
* Fix isToolMode from GenericNode to take already set value from node
* Removed tool_mode from the usePostTemplateValue params, inserting it into inly the payload
* Removing tool_mode from usePostTemplateValue use
* Made toolMode be passed in mutateTemplate
* Refactored activateToolMode to make it work between updates
* Added autoSaveFlow dependency into useEffect
* ✨ (general-bugs-save-changes-on-node.spec.ts): add test to verify that any changes made on the node are saved on user interaction
---------
Co-authored-by: cristhianzl <cristhian.lousa@gmail.com>
This commit is contained in:
parent
1f614ccca6
commit
0c2de6691d
8 changed files with 86 additions and 44 deletions
|
|
@ -49,7 +49,6 @@ export default function NodeInputField({
|
|||
node: data.node!,
|
||||
nodeId: data.id,
|
||||
parameterId: name,
|
||||
tool_mode: data.node!.tool_mode ?? false,
|
||||
});
|
||||
const setFilterEdge = useFlowStore((state) => state.setFilterEdge);
|
||||
const { handleNodeClass } = useHandleNodeClass(data.id);
|
||||
|
|
|
|||
|
|
@ -181,8 +181,10 @@ function GenericNode({
|
|||
() =>
|
||||
data.node?.outputs?.some(
|
||||
(output) => output.name === "component_as_tool",
|
||||
) ?? false,
|
||||
[data.node?.outputs],
|
||||
) ??
|
||||
data.node?.tool_mode ??
|
||||
false,
|
||||
[data.node?.outputs, data.node?.tool_mode],
|
||||
);
|
||||
|
||||
const hasToolMode = useMemo(
|
||||
|
|
|
|||
|
|
@ -20,16 +20,19 @@ export const mutateTemplate = debounce(
|
|||
setErrorData,
|
||||
parameterName?: string,
|
||||
callback?: () => void,
|
||||
toolMode?: boolean,
|
||||
) => {
|
||||
try {
|
||||
const newNode = cloneDeep(node);
|
||||
const newTemplate = await postTemplateValue.mutateAsync({
|
||||
value: newValue,
|
||||
field_name: parameterName,
|
||||
tool_mode: toolMode ?? node.tool_mode,
|
||||
});
|
||||
if (newTemplate) {
|
||||
newNode.template = newTemplate.template;
|
||||
newNode.outputs = newTemplate.outputs;
|
||||
newNode.tool_mode = toolMode;
|
||||
}
|
||||
setNodeClass(newNode);
|
||||
callback?.();
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ interface IPostTemplateValueParams {
|
|||
node: APIClassType;
|
||||
nodeId: string;
|
||||
parameterId: string;
|
||||
tool_mode: boolean;
|
||||
}
|
||||
|
||||
export const usePostTemplateValue: useMutationFunctionType<
|
||||
|
|
@ -25,7 +24,7 @@ export const usePostTemplateValue: useMutationFunctionType<
|
|||
IPostTemplateValue,
|
||||
APIClassType,
|
||||
ResponseErrorDetailAPI
|
||||
> = ({ parameterId, nodeId, node, tool_mode }, options?) => {
|
||||
> = ({ parameterId, nodeId, node }, options?) => {
|
||||
const { mutate } = UseRequestProcessor();
|
||||
|
||||
const postTemplateValueFn = async (
|
||||
|
|
@ -41,7 +40,7 @@ export const usePostTemplateValue: useMutationFunctionType<
|
|||
template: template,
|
||||
field: parameterId,
|
||||
field_value: payload.value,
|
||||
tool_mode: tool_mode,
|
||||
tool_mode: payload.tool_mode,
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@ export default function Page({ view }: { view?: boolean }): JSX.Element {
|
|||
|
||||
useEffect(() => {
|
||||
useFlowStore.setState({ autoSaveFlow });
|
||||
});
|
||||
}, [autoSaveFlow]);
|
||||
|
||||
function handleUndo(e: KeyboardEvent) {
|
||||
if (!isWrappedWithClass(e, "noflow")) {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import useHandleNodeClass from "@/CustomNodes/hooks/use-handle-node-class";
|
|||
import ShadTooltip from "@/components/common/shadTooltipComponent";
|
||||
import ToggleShadComponent from "@/components/core/parameterRenderComponent/components/toggleShadComponent";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { usePatchUpdateFlow } from "@/controllers/API/queries/flows/use-patch-update-flow";
|
||||
import { usePostTemplateValue } from "@/controllers/API/queries/nodes/use-post-template-value";
|
||||
import { usePostRetrieveVertexOrder } from "@/controllers/API/queries/vertex";
|
||||
import useAddFlow from "@/hooks/flows/use-add-flow";
|
||||
|
|
@ -93,7 +92,6 @@ const NodeToolbarComponent = memo(
|
|||
});
|
||||
},
|
||||
});
|
||||
const updateToolMode = useFlowStore((state) => state.updateToolMode);
|
||||
|
||||
const flowDataNodes = useMemo(
|
||||
() => currentFlow?.data?.nodes,
|
||||
|
|
@ -114,7 +112,6 @@ const NodeToolbarComponent = memo(
|
|||
node: data.node!,
|
||||
nodeId: data.id,
|
||||
parameterId: "tool_mode",
|
||||
tool_mode: data.node!.tool_mode ?? false,
|
||||
});
|
||||
|
||||
const isSaved = flows?.some((flow) =>
|
||||
|
|
@ -141,8 +138,6 @@ const NodeToolbarComponent = memo(
|
|||
);
|
||||
const addFlow = useAddFlow();
|
||||
|
||||
const { mutate: patchUpdateFlow } = usePatchUpdateFlow();
|
||||
|
||||
const isMinimal = useMemo(
|
||||
() => countHandlesFn(data) <= 1 && numberOfOutputHandles <= 1,
|
||||
[data, numberOfOutputHandles],
|
||||
|
|
@ -172,13 +167,8 @@ const NodeToolbarComponent = memo(
|
|||
};
|
||||
|
||||
const handleActivateToolMode = () => {
|
||||
const newValue = !flowDataNodes![index]!.data.node.tool_mode;
|
||||
|
||||
updateToolMode(data.id, newValue);
|
||||
data.node!.tool_mode = newValue;
|
||||
|
||||
const newValue = !toolMode;
|
||||
setToolMode(newValue);
|
||||
|
||||
mutateTemplate(
|
||||
newValue,
|
||||
data.node!,
|
||||
|
|
@ -186,21 +176,9 @@ const NodeToolbarComponent = memo(
|
|||
postToolModeValue,
|
||||
setErrorData,
|
||||
"tool_mode",
|
||||
() => {
|
||||
currentFlow!.data!.nodes[index]!.data.node.tool_mode = newValue;
|
||||
patchUpdateFlow({
|
||||
id: currentFlow?.id!,
|
||||
name: currentFlow?.name!,
|
||||
data: currentFlow?.data!,
|
||||
description: currentFlow?.description!,
|
||||
folder_id: currentFlow?.folder_id!,
|
||||
endpoint_name: currentFlow?.endpoint_name!,
|
||||
});
|
||||
},
|
||||
() => updateNodeInternals(data.id),
|
||||
newValue,
|
||||
);
|
||||
|
||||
updateNodeInternals(data.id);
|
||||
return newValue;
|
||||
};
|
||||
|
||||
const handleMinimize = useCallback(() => {
|
||||
|
|
@ -437,6 +415,7 @@ const NodeToolbarComponent = memo(
|
|||
setLastCopiedSelection,
|
||||
paste,
|
||||
handleActivateToolMode,
|
||||
toolMode,
|
||||
],
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -294,32 +294,35 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
? change(get().nodes.find((node) => node.id === id)!)
|
||||
: change;
|
||||
|
||||
set((state) => {
|
||||
const newNodes = state.nodes.map((node) => {
|
||||
if (node.id === id) {
|
||||
if (isUserChange) {
|
||||
if ((node.data as NodeDataType).node?.frozen) {
|
||||
(newChange.data as NodeDataType).node!.frozen = false;
|
||||
}
|
||||
const newNodes = get().nodes.map((node) => {
|
||||
if (node.id === id) {
|
||||
if (isUserChange) {
|
||||
if ((node.data as NodeDataType).node?.frozen) {
|
||||
(newChange.data as NodeDataType).node!.frozen = false;
|
||||
}
|
||||
return newChange;
|
||||
}
|
||||
return node;
|
||||
});
|
||||
return newChange;
|
||||
}
|
||||
return node;
|
||||
});
|
||||
|
||||
const newEdges = cleanEdges(newNodes, get().edges);
|
||||
const newEdges = cleanEdges(newNodes, get().edges);
|
||||
|
||||
set((state) => {
|
||||
if (callback) {
|
||||
// Defer the callback execution to ensure it runs after state updates are fully applied.
|
||||
queueMicrotask(callback);
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
nodes: newNodes,
|
||||
edges: newEdges,
|
||||
};
|
||||
});
|
||||
get().updateCurrentFlow({ nodes: newNodes, edges: newEdges });
|
||||
if (get().autoSaveFlow) {
|
||||
get().autoSaveFlow!();
|
||||
}
|
||||
},
|
||||
getNode: (id: string) => {
|
||||
return get().nodes.find((node) => node.id === id);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
import { expect, Page, test } from "@playwright/test";
|
||||
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
|
||||
|
||||
async function verifyTextareaValue(page: Page, value: string) {
|
||||
await page.getByTestId("textarea_str_input_value").fill(value);
|
||||
await page.getByTestId("icon-ChevronLeft").first().click();
|
||||
|
||||
await page.waitForSelector('[data-testid="list-card"]', {
|
||||
timeout: 3000,
|
||||
});
|
||||
|
||||
await page.getByTestId("list-card").first().click();
|
||||
|
||||
await page.waitForSelector('[data-testid="textarea_str_input_value"]', {
|
||||
timeout: 3000,
|
||||
});
|
||||
|
||||
const inputValue = await page
|
||||
.getByTestId("textarea_str_input_value")
|
||||
.inputValue();
|
||||
expect(inputValue).toBe(value);
|
||||
}
|
||||
|
||||
test(
|
||||
"any changes on the node must be saved on user interaction",
|
||||
{ tag: ["@release", "@components"] },
|
||||
async ({ page }) => {
|
||||
const randomValues = Array.from({ length: 4 }, () =>
|
||||
Math.random().toString(36).substring(2, 15),
|
||||
);
|
||||
|
||||
await awaitBootstrapTest(page);
|
||||
await page.getByTestId("blank-flow").click();
|
||||
|
||||
await page.waitForSelector('[data-testid="fit_view"]', {
|
||||
timeout: 100000,
|
||||
});
|
||||
await page.getByTestId("sidebar-search-input").click();
|
||||
await page.getByTestId("sidebar-search-input").fill("text output");
|
||||
|
||||
await page
|
||||
.getByTestId("outputsText Output")
|
||||
.hover()
|
||||
.then(async () => {
|
||||
await page.getByTestId("add-component-button-text-output").click();
|
||||
});
|
||||
|
||||
await page.waitForSelector('[data-testid="title-Text Output"]', {
|
||||
timeout: 3000,
|
||||
});
|
||||
|
||||
// Verify each random value
|
||||
for (const value of randomValues) {
|
||||
await verifyTextareaValue(page, value);
|
||||
}
|
||||
},
|
||||
);
|
||||
Loading…
Add table
Add a link
Reference in a new issue