🚧 Added Vuex dirty state flag as central source of truth for if there are unsaved changes

This commit is contained in:
Rupenieks 2020-09-01 16:06:08 +02:00
parent 66b76d16ee
commit 3e1ada7c1a
8 changed files with 92 additions and 49 deletions

View file

@ -12,7 +12,7 @@
<font-awesome-icon icon="check" class="execution-icon success" v-if="executionFinished" title="Execution was successful" /> <font-awesome-icon icon="check" class="execution-icon success" v-if="executionFinished" title="Execution was successful" />
<font-awesome-icon icon="times" class="execution-icon error" v-else title="Execution did fail" /> <font-awesome-icon icon="times" class="execution-icon error" v-else title="Execution did fail" />
</span> </span>
of of
<span class="workflow-name clickable" title="Open Workflow"> <span class="workflow-name clickable" title="Open Workflow">
<span @click="openWorkflow(workflowExecution.workflowId)">"{{workflowName}}"</span> <span @click="openWorkflow(workflowExecution.workflowId)">"{{workflowName}}"</span>
</span> </span>
@ -154,6 +154,9 @@ export default mixins(
workflowRunning (): boolean { workflowRunning (): boolean {
return this.$store.getters.isActionActive('workflowRunning'); return this.$store.getters.isActionActive('workflowRunning');
}, },
isDirty () : boolean {
return this.$store.getters.getStateIsDirty;
},
}, },
methods: { methods: {
async openWorkflow (workflowId: string) { async openWorkflow (workflowId: string) {

View file

@ -398,7 +398,7 @@ export default mixins(
return; return;
} }
this.$store.commit('setWorkflowName', workflowName); this.$store.commit('setWorkflowName', {newName: workflowName, setStateDirty: true});
this.$showMessage({ this.$showMessage({
title: 'Workflow renamed', title: 'Workflow renamed',
@ -440,18 +440,15 @@ export default mixins(
saveAs(blob, workflowName + '.json'); saveAs(blob, workflowName + '.json');
} else if (key === 'workflow-save') { } else if (key === 'workflow-save') {
console.log("saving......");
this.saveCurrentWorkflow(); this.saveCurrentWorkflow();
} else if (key === 'workflow-save-as') { } else if (key === 'workflow-save-as') {
console.log("saving......");
this.saveCurrentWorkflow(true); this.saveCurrentWorkflow(true);
} else if (key === 'help-about') { } else if (key === 'help-about') {
this.aboutDialogVisible = true; this.aboutDialogVisible = true;
} else if (key === 'workflow-settings') { } else if (key === 'workflow-settings') {
this.workflowSettingsDialogVisible = true; this.workflowSettingsDialogVisible = true;
} else if (key === 'workflow-new') { } else if (key === 'workflow-new') {
const workflowId = this.$store.getters.workflowId; const result = this.$store.getters.getStateIsDirty;
const result = await this.dataHasChanged(workflowId);
if(result) { if(result) {
const importConfirm = await this.confirmMessage(`When you switch workflows your current workflow changes will be lost.`, 'Save your Changes?', 'warning', 'Yes, switch workflows and forget changes'); const importConfirm = await this.confirmMessage(`When you switch workflows your current workflow changes will be lost.`, 'Save your Changes?', 'warning', 'Yes, switch workflows and forget changes');
if (importConfirm === true) { if (importConfirm === true) {

View file

@ -92,8 +92,7 @@ export default mixins(
}, },
async openWorkflow (data: IWorkflowShortResponse, column: any) { // tslint:disable-line:no-any async openWorkflow (data: IWorkflowShortResponse, column: any) { // tslint:disable-line:no-any
if (column.label !== 'Active') { if (column.label !== 'Active') {
const workflowId = this.$store.getters.workflowId; const result = this.$store.getters.getStateIsDirty;
const result = await this.dataHasChanged(workflowId);
if(result) { if(result) {
const importConfirm = await this.confirmMessage(`When you switch workflows your current workflow changes will be lost.`, 'Save your Changes?', 'warning', 'Yes, switch workflows and forget changes'); const importConfirm = await this.confirmMessage(`When you switch workflows your current workflow changes will be lost.`, 'Save your Changes?', 'warning', 'Yes, switch workflows and forget changes');
if (importConfirm === false) { if (importConfirm === false) {

View file

@ -31,7 +31,7 @@ export const moveNodeWorkflow = mixins(nodeIndex).extend({
const nodeViewOffsetPositionX = offsetPosition[0] + (e.pageX - this.moveLastPosition[0]); const nodeViewOffsetPositionX = offsetPosition[0] + (e.pageX - this.moveLastPosition[0]);
const nodeViewOffsetPositionY = offsetPosition[1] + (e.pageY - this.moveLastPosition[1]); const nodeViewOffsetPositionY = offsetPosition[1] + (e.pageY - this.moveLastPosition[1]);
this.$store.commit('setNodeViewOffsetPosition', [nodeViewOffsetPositionX, nodeViewOffsetPositionY]); this.$store.commit('setNodeViewOffsetPosition', {newOffset: [nodeViewOffsetPositionX, nodeViewOffsetPositionY], setStateDirty: true});
// Update the last position // Update the last position
this.moveLastPosition[0] = e.pageX; this.moveLastPosition[0] = e.pageX;
@ -87,7 +87,7 @@ export const moveNodeWorkflow = mixins(nodeIndex).extend({
const offsetPosition = this.$store.getters.getNodeViewOffsetPosition; const offsetPosition = this.$store.getters.getNodeViewOffsetPosition;
const nodeViewOffsetPositionX = offsetPosition[0] - e.deltaX; const nodeViewOffsetPositionX = offsetPosition[0] - e.deltaX;
const nodeViewOffsetPositionY = offsetPosition[1] - e.deltaY; const nodeViewOffsetPositionY = offsetPosition[1] - e.deltaY;
this.$store.commit('setNodeViewOffsetPosition', [nodeViewOffsetPositionX, nodeViewOffsetPositionY]); this.$store.commit('setNodeViewOffsetPosition', {newOffset: [nodeViewOffsetPositionX, nodeViewOffsetPositionY], setStateDirty: true});
}, },
}, },
}); });

View file

@ -420,7 +420,7 @@ export const workflowHelpers = mixins(
this.$store.commit('setActive', workflowData.active || false); this.$store.commit('setActive', workflowData.active || false);
this.$store.commit('setWorkflowId', workflowData.id); this.$store.commit('setWorkflowId', workflowData.id);
this.$store.commit('setWorkflowName', workflowData.name); this.$store.commit('setWorkflowName', {newName: workflowData.name, setStateDirty: false});
this.$store.commit('setWorkflowSettings', workflowData.settings || {}); this.$store.commit('setWorkflowSettings', workflowData.settings || {});
} else { } else {
// Workflow exists already so update it // Workflow exists already so update it
@ -435,7 +435,7 @@ export const workflowHelpers = mixins(
} }
this.$store.commit('removeActiveAction', 'workflowSaving'); this.$store.commit('removeActiveAction', 'workflowSaving');
this.$store.commit('setStateDirty', false);
this.$showMessage({ this.$showMessage({
title: 'Workflow saved', title: 'Workflow saved',
message: `The workflow "${workflowData.name}" got saved!`, message: `The workflow "${workflowData.name}" got saved!`,
@ -492,13 +492,13 @@ export const workflowHelpers = mixins(
nodes: data.nodes, nodes: data.nodes,
connections: data.connections, connections: data.connections,
settings: data.settings, settings: data.settings,
name: data.name name: data.name,
}; };
const y = { const y = {
nodes: currentData.nodes, nodes: currentData.nodes,
connections: currentData.connections, connections: currentData.connections,
settings: currentData.settings, settings: currentData.settings,
name: currentData.name name: currentData.name,
}; };
return !isEqual(x, y); return !isEqual(x, y);
} }

View file

@ -74,7 +74,7 @@ export const workflowSave = mixins(
this.$store.commit('setActive', workflowData.active || false); this.$store.commit('setActive', workflowData.active || false);
this.$store.commit('setWorkflowId', workflowData.id); this.$store.commit('setWorkflowId', workflowData.id);
this.$store.commit('setWorkflowName', workflowData.name); this.$store.commit('setWorkflowName', {newName: workflowData.name, setStateDirty: false});
this.$store.commit('setWorkflowSettings', workflowData.settings || {}); this.$store.commit('setWorkflowSettings', workflowData.settings || {});
} else { } else {
// Workflow exists already so update it // Workflow exists already so update it
@ -89,7 +89,7 @@ export const workflowSave = mixins(
} }
this.$store.commit('removeActiveAction', 'workflowSaving'); this.$store.commit('removeActiveAction', 'workflowSaving');
this.$store.commit('setStateDirty', false);
this.$showMessage({ this.$showMessage({
title: 'Workflow saved', title: 'Workflow saved',
message: `The workflow "${workflowData.name}" got saved!`, message: `The workflow "${workflowData.name}" got saved!`,

View file

@ -52,6 +52,7 @@ export const store = new Vuex.Store({
saveDataSuccessExecution: 'all', saveDataSuccessExecution: 'all',
saveManualExecutions: false, saveManualExecutions: false,
timezone: 'America/New_York', timezone: 'America/New_York',
stateIsDirty: false,
executionTimeout: -1, executionTimeout: -1,
maxExecutionTimeout: Number.MAX_SAFE_INTEGER, maxExecutionTimeout: Number.MAX_SAFE_INTEGER,
versionCli: '0.0.0', versionCli: '0.0.0',
@ -83,6 +84,7 @@ export const store = new Vuex.Store({
state.activeActions.push(action); state.activeActions.push(action);
} }
}, },
removeActiveAction (state, action: string) { removeActiveAction (state, action: string) {
const actionIndex = state.activeActions.indexOf(action); const actionIndex = state.activeActions.indexOf(action);
if (actionIndex !== -1) { if (actionIndex !== -1) {
@ -92,6 +94,7 @@ export const store = new Vuex.Store({
// Active Executions // Active Executions
addActiveExecution (state, newActiveExecution: IExecutionsCurrentSummaryExtended) { addActiveExecution (state, newActiveExecution: IExecutionsCurrentSummaryExtended) {
state.stateIsDirty = true;
// Check if the execution exists already // Check if the execution exists already
const activeExecution = state.activeExecutions.find(execution => { const activeExecution = state.activeExecutions.find(execution => {
return execution.idActive === newActiveExecution.idActive; return execution.idActive === newActiveExecution.idActive;
@ -108,6 +111,7 @@ export const store = new Vuex.Store({
state.activeExecutions.unshift(newActiveExecution); state.activeExecutions.unshift(newActiveExecution);
}, },
finishActiveExecution (state, finishedActiveExecution: IPushDataExecutionFinished) { finishActiveExecution (state, finishedActiveExecution: IPushDataExecutionFinished) {
state.stateIsDirty = true;
// Find the execution to set to finished // Find the execution to set to finished
const activeExecution = state.activeExecutions.find(execution => { const activeExecution = state.activeExecutions.find(execution => {
return execution.idActive === finishedActiveExecution.executionIdActive; return execution.idActive === finishedActiveExecution.executionIdActive;
@ -126,6 +130,7 @@ export const store = new Vuex.Store({
Vue.set(activeExecution, 'stoppedAt', finishedActiveExecution.data.stoppedAt); Vue.set(activeExecution, 'stoppedAt', finishedActiveExecution.data.stoppedAt);
}, },
setActiveExecutions (state, newActiveExecutions: IExecutionsCurrentSummaryExtended[]) { setActiveExecutions (state, newActiveExecutions: IExecutionsCurrentSummaryExtended[]) {
state.stateIsDirty = true;
Vue.set(state, 'activeExecutions', newActiveExecutions); Vue.set(state, 'activeExecutions', newActiveExecutions);
}, },
@ -134,23 +139,32 @@ export const store = new Vuex.Store({
state.activeWorkflows = newActiveWorkflows; state.activeWorkflows = newActiveWorkflows;
}, },
setWorkflowActive (state, workflowId: string) { setWorkflowActive (state, workflowId: string) {
state.stateIsDirty = true;
const index = state.activeWorkflows.indexOf(workflowId); const index = state.activeWorkflows.indexOf(workflowId);
if (index === -1) { if (index === -1) {
state.activeWorkflows.push(workflowId); state.activeWorkflows.push(workflowId);
} }
}, },
setWorkflowInactive (state, workflowId: string) { setWorkflowInactive (state, workflowId: string) {
state.stateIsDirty = true;
const index = state.activeWorkflows.indexOf(workflowId); const index = state.activeWorkflows.indexOf(workflowId);
if (index !== -1) { if (index !== -1) {
state.selectedNodes.splice(index, 1); state.selectedNodes.splice(index, 1);
} }
}, },
// Set state condition dirty or not
// ** Dirty: if current workflow state has been synchronized with database AKA has it been saved
setStateDirty (state, dirty : boolean) {
state.stateIsDirty = dirty;
},
// Selected Nodes // Selected Nodes
addSelectedNode (state, node: INodeUi) { addSelectedNode (state, node: INodeUi) {
state.stateIsDirty = true;
state.selectedNodes.push(node); state.selectedNodes.push(node);
}, },
removeNodeFromSelection (state, node: INodeUi) { removeNodeFromSelection (state, node: INodeUi) {
state.stateIsDirty = true;
let index; let index;
for (index in state.selectedNodes) { for (index in state.selectedNodes) {
if (state.selectedNodes[index].name === node.name) { if (state.selectedNodes[index].name === node.name) {
@ -176,6 +190,10 @@ export const store = new Vuex.Store({
return; return;
} }
if (data.setStateDirty) {
state.stateIsDirty = true;
}
const sourceData: IConnection = data.connection[0]; const sourceData: IConnection = data.connection[0];
const destinationData: IConnection = data.connection[1]; const destinationData: IConnection = data.connection[1];
@ -211,6 +229,7 @@ export const store = new Vuex.Store({
if (connectionExists === false) { if (connectionExists === false) {
state.workflow.connections[sourceData.node][sourceData.type][sourceData.index].push(destinationData); state.workflow.connections[sourceData.node][sourceData.type][sourceData.index].push(destinationData);
} }
}, },
removeConnection (state, data) { removeConnection (state, data) {
const sourceData = data.connection[0]; const sourceData = data.connection[0];
@ -226,6 +245,8 @@ export const store = new Vuex.Store({
return; return;
} }
state.stateIsDirty = true;
const connections = state.workflow.connections[sourceData.node][sourceData.type][sourceData.index]; const connections = state.workflow.connections[sourceData.node][sourceData.type][sourceData.index];
for (const index in connections) { for (const index in connections) {
if (connections[index].node === destinationData.node && connections[index].type === destinationData.type && connections[index].index === destinationData.index) { if (connections[index].node === destinationData.node && connections[index].type === destinationData.type && connections[index].index === destinationData.index) {
@ -233,11 +254,16 @@ export const store = new Vuex.Store({
connections.splice(parseInt(index, 10), 1); connections.splice(parseInt(index, 10), 1);
} }
} }
}, },
removeAllConnections (state) { removeAllConnections (state, data) {
if (data.setStateDirty === true) {
state.stateIsDirty = true;
}
state.workflow.connections = {}; state.workflow.connections = {};
}, },
removeAllNodeConnection (state, node: INodeUi) { removeAllNodeConnection (state, node: INodeUi) {
state.stateIsDirty = true;
// Remove all source connections // Remove all source connections
if (state.workflow.connections.hasOwnProperty(node.name)) { if (state.workflow.connections.hasOwnProperty(node.name)) {
delete state.workflow.connections[node.name]; delete state.workflow.connections[node.name];
@ -275,6 +301,7 @@ export const store = new Vuex.Store({
if (state.credentials === null) { if (state.credentials === null) {
return; return;
} }
for (let i = 0; i < state.credentials.length; i++) { for (let i = 0; i < state.credentials.length; i++) {
if (state.credentials[i].id === credentialData.id) { if (state.credentials[i].id === credentialData.id) {
state.credentials.splice(i, 1); state.credentials.splice(i, 1);
@ -286,6 +313,7 @@ export const store = new Vuex.Store({
if (state.credentials === null) { if (state.credentials === null) {
return; return;
} }
for (let i = 0; i < state.credentials.length; i++) { for (let i = 0; i < state.credentials.length; i++) {
if (state.credentials[i].id === credentialData.id) { if (state.credentials[i].id === credentialData.id) {
state.credentials[i] = credentialData; state.credentials[i] = credentialData;
@ -301,6 +329,7 @@ export const store = new Vuex.Store({
}, },
renameNodeSelectedAndExecution (state, nameData) { renameNodeSelectedAndExecution (state, nameData) {
state.stateIsDirty = true;
// If node has any WorkflowResultData rename also that one that the data // If node has any WorkflowResultData rename also that one that the data
// does still get displayed also after node got renamed // does still get displayed also after node got renamed
if (state.workflowExecutionData !== null && state.workflowExecutionData.data.resultData.runData.hasOwnProperty(nameData.old)) { if (state.workflowExecutionData !== null && state.workflowExecutionData.data.resultData.runData.hasOwnProperty(nameData.old)) {
@ -318,10 +347,12 @@ export const store = new Vuex.Store({
state.workflow.nodes.forEach((node) => { state.workflow.nodes.forEach((node) => {
node.issues = undefined; node.issues = undefined;
}); });
return true; return true;
}, },
setNodeIssue (state, nodeIssueData: INodeIssueData) { setNodeIssue (state, nodeIssueData: INodeIssueData) {
const node = state.workflow.nodes.find(node => { const node = state.workflow.nodes.find(node => {
return node.name === nodeIssueData.node; return node.name === nodeIssueData.node;
}); });
@ -345,6 +376,7 @@ export const store = new Vuex.Store({
// Set/Overwrite the value // Set/Overwrite the value
Vue.set(node.issues!, nodeIssueData.type, nodeIssueData.value); Vue.set(node.issues!, nodeIssueData.type, nodeIssueData.value);
state.stateIsDirty = true;
} }
return true; return true;
@ -356,8 +388,11 @@ export const store = new Vuex.Store({
}, },
// Name // Name
setWorkflowName (state, newName: string) { setWorkflowName (state, data) {
state.workflow.name = newName; if (data.setStateDirty === true) {
state.stateIsDirty = true;
}
state.workflow.name = data.newName;
}, },
// Nodes // Nodes
@ -374,11 +409,15 @@ export const store = new Vuex.Store({
for (let i = 0; i < state.workflow.nodes.length; i++) { for (let i = 0; i < state.workflow.nodes.length; i++) {
if (state.workflow.nodes[i].name === node.name) { if (state.workflow.nodes[i].name === node.name) {
state.workflow.nodes.splice(i, 1); state.workflow.nodes.splice(i, 1);
state.stateIsDirty = true;
return; return;
} }
} }
}, },
removeAllNodes (state) { removeAllNodes (state, data) {
if (data.setStateDirty === true) {
state.stateIsDirty = true;
}
state.workflow.nodes.splice(0, state.workflow.nodes.length); state.workflow.nodes.splice(0, state.workflow.nodes.length);
}, },
updateNodeProperties (state, updateInformation: INodeUpdatePropertiesInformation) { updateNodeProperties (state, updateInformation: INodeUpdatePropertiesInformation) {
@ -388,6 +427,7 @@ export const store = new Vuex.Store({
}); });
if (node) { if (node) {
state.stateIsDirty = true;
for (const key of Object.keys(updateInformation.properties)) { for (const key of Object.keys(updateInformation.properties)) {
Vue.set(node, key, updateInformation.properties[key]); Vue.set(node, key, updateInformation.properties[key]);
} }
@ -403,6 +443,7 @@ export const store = new Vuex.Store({
throw new Error(`Node with the name "${updateInformation.name}" could not be found to set parameter.`); throw new Error(`Node with the name "${updateInformation.name}" could not be found to set parameter.`);
} }
state.stateIsDirty = true;
Vue.set(node, updateInformation.key, updateInformation.value); Vue.set(node, updateInformation.key, updateInformation.value);
}, },
setNodeParameters (state, updateInformation: IUpdateInformation) { setNodeParameters (state, updateInformation: IUpdateInformation) {
@ -415,6 +456,7 @@ export const store = new Vuex.Store({
throw new Error(`Node with the name "${updateInformation.name}" could not be found to set parameter.`); throw new Error(`Node with the name "${updateInformation.name}" could not be found to set parameter.`);
} }
state.stateIsDirty = true;
Vue.set(node, 'parameters', updateInformation.value); Vue.set(node, 'parameters', updateInformation.value);
}, },
@ -423,6 +465,7 @@ export const store = new Vuex.Store({
state.nodeIndex.push(nodeName); state.nodeIndex.push(nodeName);
}, },
setNodeIndex (state, newData: { index: number, name: string | null}) { setNodeIndex (state, newData: { index: number, name: string | null}) {
state.stateIsDirty = true;
state.nodeIndex[newData.index] = newData.name; state.nodeIndex[newData.index] = newData.name;
}, },
resetNodeIndex (state) { resetNodeIndex (state) {
@ -433,8 +476,11 @@ export const store = new Vuex.Store({
setNodeViewMoveInProgress (state, value: boolean) { setNodeViewMoveInProgress (state, value: boolean) {
state.nodeViewMoveInProgress = value; state.nodeViewMoveInProgress = value;
}, },
setNodeViewOffsetPosition (state, newOffset: XYPositon) { setNodeViewOffsetPosition (state, data) {
state.nodeViewOffsetPosition = newOffset; if (data.setStateDirty === true) {
state.stateIsDirty = true;
}
state.nodeViewOffsetPosition = data.newOffset;
}, },
// Node-Types // Node-Types
@ -497,19 +543,22 @@ export const store = new Vuex.Store({
// TODO: Check if there is an error or whatever that is supposed to be returned // TODO: Check if there is an error or whatever that is supposed to be returned
return; return;
} }
state.stateIsDirty = true;
state.nodeTypes.push(typeData); state.nodeTypes.push(typeData);
}, },
setActiveNode (state, nodeName: string) { setActiveNode (state, nodeName: string) {
state.stateIsDirty = true;
state.activeNode = nodeName; state.activeNode = nodeName;
}, },
setLastSelectedNode (state, nodeName: string) { setLastSelectedNode (state, nodeName: string) {
state.stateIsDirty = true;
state.lastSelectedNode = nodeName; state.lastSelectedNode = nodeName;
}, },
setLastSelectedNodeOutputIndex (state, outputIndex: number | null) { setLastSelectedNodeOutputIndex (state, outputIndex: number | null) {
state.stateIsDirty = true;
state.lastSelectedNodeOutputIndex = outputIndex; state.lastSelectedNodeOutputIndex = outputIndex;
}, },
@ -523,7 +572,7 @@ export const store = new Vuex.Store({
if (state.workflowExecutionData.data.resultData.runData[pushData.nodeName] === undefined) { if (state.workflowExecutionData.data.resultData.runData[pushData.nodeName] === undefined) {
Vue.set(state.workflowExecutionData.data.resultData.runData, pushData.nodeName, []); Vue.set(state.workflowExecutionData.data.resultData.runData, pushData.nodeName, []);
} }
state.stateIsDirty = true;
state.workflowExecutionData.data.resultData.runData[pushData.nodeName].push(pushData.data); state.workflowExecutionData.data.resultData.runData[pushData.nodeName].push(pushData.data);
}, },
@ -588,6 +637,10 @@ export const store = new Vuex.Store({
return `${state.urlBaseWebhook}${state.endpointWebhookTest}`; return `${state.urlBaseWebhook}${state.endpointWebhookTest}`;
}, },
getStateIsDirty: (state) : boolean => {
return state.stateIsDirty;
},
saveDataErrorExecution: (state): string => { saveDataErrorExecution: (state): string => {
return state.saveDataErrorExecution; return state.saveDataErrorExecution;
}, },

View file

@ -127,7 +127,7 @@ import NodeSettings from '@/components/NodeSettings.vue';
import RunData from '@/components/RunData.vue'; import RunData from '@/components/RunData.vue';
import mixins from 'vue-typed-mixins'; import mixins from 'vue-typed-mixins';
import { uuid } from 'uuidv4';
import { debounce, isEqual } from 'lodash'; import { debounce, isEqual } from 'lodash';
import axios from 'axios'; import axios from 'axios';
import { import {
@ -196,13 +196,8 @@ export default mixins(
if (this.$route && this.$route.params.name) { if (this.$route && this.$route.params.name) {
workflowId = this.$route.params.name; workflowId = this.$route.params.name;
} }
if(workflowId !== null) {
this.isDirty = await this.dataHasChanged(workflowId);
} else {
this.isDirty = true;
}
}, },
deep: true deep: true,
}, },
connections: { connections: {
async handler (val, oldVal) { async handler (val, oldVal) {
@ -211,13 +206,8 @@ export default mixins(
if (this.$route && this.$route.params.name) { if (this.$route && this.$route.params.name) {
workflowId = this.$route.params.name; workflowId = this.$route.params.name;
} }
if(workflowId !== null) {
this.isDirty = await this.dataHasChanged(workflowId);
} else {
this.isDirty = true;
}
}, },
deep: true deep: true,
}, },
}, },
computed: { computed: {
@ -292,7 +282,6 @@ export default mixins(
ctrlKeyPressed: false, ctrlKeyPressed: false,
debouncedFunctions: [] as any[], // tslint:disable-line:no-any debouncedFunctions: [] as any[], // tslint:disable-line:no-any
stopExecutionInProgress: false, stopExecutionInProgress: false,
isDirty: false,
}; };
}, },
beforeDestroy () { beforeDestroy () {
@ -336,7 +325,7 @@ export default mixins(
throw new Error(`Execution with id "${executionId}" could not be found!`); throw new Error(`Execution with id "${executionId}" could not be found!`);
} }
this.$store.commit('setWorkflowName', data.workflowData.name); this.$store.commit('setWorkflowName', {newName: data.workflowData.name, setStateDirty: false});
this.$store.commit('setWorkflowId', PLACEHOLDER_EMPTY_WORKFLOW_ID); this.$store.commit('setWorkflowId', PLACEHOLDER_EMPTY_WORKFLOW_ID);
this.$store.commit('setWorkflowExecutionData', data); this.$store.commit('setWorkflowExecutionData', data);
@ -360,7 +349,7 @@ export default mixins(
this.$store.commit('setActive', data.active || false); this.$store.commit('setActive', data.active || false);
this.$store.commit('setWorkflowId', workflowId); this.$store.commit('setWorkflowId', workflowId);
this.$store.commit('setWorkflowName', data.name); this.$store.commit('setWorkflowName', {newName: data.name, setStateDirty: false});
this.$store.commit('setWorkflowSettings', data.settings || {}); this.$store.commit('setWorkflowSettings', data.settings || {});
await this.addNodes(data.nodes, data.connections); await this.addNodes(data.nodes, data.connections);
@ -467,7 +456,7 @@ export default mixins(
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
this.isDirty = false; this.$store.commit('setStateDirty', false);
this.callDebounced('saveCurrentWorkflow', 1000); this.callDebounced('saveCurrentWorkflow', 1000);
} else if (e.key === 'Enter') { } else if (e.key === 'Enter') {
@ -985,7 +974,7 @@ export default mixins(
newNodeData.name = this.getUniqueNodeName(newNodeData.name); newNodeData.name = this.getUniqueNodeName(newNodeData.name);
if (nodeTypeData.webhooks && nodeTypeData.webhooks.length) { if (nodeTypeData.webhooks && nodeTypeData.webhooks.length) {
newNodeData.webhookId = uuidv4(); newNodeData.webhookId = uuid();
} }
await this.addNodes([newNodeData]); await this.addNodes([newNodeData]);
@ -1345,7 +1334,7 @@ export default mixins(
if (this.$route.params.action === 'workflowSave') { if (this.$route.params.action === 'workflowSave') {
// In case the workflow got saved we do not have to run init // In case the workflow got saved we do not have to run init
// as only the route changed but all the needed data is already loaded // as only the route changed but all the needed data is already loaded
this.isDirty = false; this.$store.commit('setStateDirty', false);
return Promise.resolve(); return Promise.resolve();
} }
@ -1375,7 +1364,7 @@ export default mixins(
document.addEventListener('keyup', this.keyUp); document.addEventListener('keyup', this.keyUp);
window.addEventListener("beforeunload", (e) => { window.addEventListener("beforeunload", (e) => {
if(this.isDirty === true) { if(this.$store.getters.getStateIsDirty === true) {
const confirmationMessage = 'It looks like you have been editing something. ' const confirmationMessage = 'It looks like you have been editing something. '
+ 'If you leave before saving, your changes will be lost.'; + 'If you leave before saving, your changes will be lost.';
(e || window.event).returnValue = confirmationMessage; //Gecko + IE (e || window.event).returnValue = confirmationMessage; //Gecko + IE
@ -1399,6 +1388,8 @@ export default mixins(
detachable: !this.isReadOnly, detachable: !this.isReadOnly,
}); });
} else { } else {
// @ts-ignore
connection.setStateDirty = false;
// When nodes get connected it gets saved automatically to the storage // When nodes get connected it gets saved automatically to the storage
// so if we do not connect we have to save the connection manually // so if we do not connect we have to save the connection manually
this.$store.commit('addConnection', { connection }); this.$store.commit('addConnection', { connection });
@ -1586,7 +1577,7 @@ export default mixins(
this.instance.deleteEveryEndpoint(); this.instance.deleteEveryEndpoint();
} }
this.$store.commit('removeAllConnections'); this.$store.commit('removeAllConnections');
this.$store.commit('removeAllNodes'); this.$store.commit('removeAllNodes', {setStateDirty: true});
// Wait a tick that the old nodes had time to get removed // Wait a tick that the old nodes had time to get removed
await Vue.nextTick(); await Vue.nextTick();
@ -1876,8 +1867,8 @@ export default mixins(
}); });
} }
this.$store.commit('removeAllConnections'); this.$store.commit('removeAllConnections', {setStateDirty: false});
this.$store.commit('removeAllNodes'); this.$store.commit('removeAllNodes', {setStateDirty: false});
// Reset workflow execution data // Reset workflow execution data
this.$store.commit('setWorkflowExecutionData', null); this.$store.commit('setWorkflowExecutionData', null);
@ -1886,7 +1877,7 @@ export default mixins(
this.$store.commit('setActive', false); this.$store.commit('setActive', false);
this.$store.commit('setWorkflowId', PLACEHOLDER_EMPTY_WORKFLOW_ID); this.$store.commit('setWorkflowId', PLACEHOLDER_EMPTY_WORKFLOW_ID);
this.$store.commit('setWorkflowName', ''); this.$store.commit('setWorkflowName', {newName: '', setStateDirty: false});
this.$store.commit('setWorkflowSettings', {}); this.$store.commit('setWorkflowSettings', {});
this.$store.commit('setActiveExecutionId', null); this.$store.commit('setActiveExecutionId', null);
@ -1897,7 +1888,7 @@ export default mixins(
this.$store.commit('resetNodeIndex'); this.$store.commit('resetNodeIndex');
this.$store.commit('resetSelectedNodes'); this.$store.commit('resetSelectedNodes');
this.$store.commit('setNodeViewOffsetPosition', [0, 0]); this.$store.commit('setNodeViewOffsetPosition', {newOffset: [0, 0], setStateDirty: false});
return Promise.resolve(); return Promise.resolve();
}, },