mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
🚧 Added Vuex dirty state flag as central source of truth for if there are unsaved changes
This commit is contained in:
parent
66b76d16ee
commit
3e1ada7c1a
|
@ -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) {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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!`,
|
||||||
|
|
|
@ -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;
|
||||||
},
|
},
|
||||||
|
|
|
@ -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();
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue