diff --git a/packages/editor-ui/src/components/MainSidebar.vue b/packages/editor-ui/src/components/MainSidebar.vue index a54b530588..ea2a8b4cef 100644 --- a/packages/editor-ui/src/components/MainSidebar.vue +++ b/packages/editor-ui/src/components/MainSidebar.vue @@ -440,21 +440,38 @@ export default mixins( saveAs(blob, workflowName + '.json'); } else if (key === 'workflow-save') { + console.log("saving......"); this.saveCurrentWorkflow(); } else if (key === 'workflow-save-as') { + console.log("saving......"); this.saveCurrentWorkflow(true); } else if (key === 'help-about') { this.aboutDialogVisible = true; } else if (key === 'workflow-settings') { this.workflowSettingsDialogVisible = true; } else if (key === 'workflow-new') { - this.$router.push({ name: 'NodeViewNew' }); + const workflowId = this.$store.getters.workflowId; + const result = await this.dataHasChanged(workflowId); + 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'); + if (importConfirm === true) { + this.$router.push({ name: 'NodeViewNew' }); - this.$showMessage({ - title: 'Workflow created', - message: 'A new workflow got created!', - type: 'success', - }); + this.$showMessage({ + title: 'Workflow created', + message: 'A new workflow got created!', + type: 'success', + }); + } + } else { + this.$router.push({ name: 'NodeViewNew' }); + + this.$showMessage({ + title: 'Workflow created', + message: 'A new workflow got created!', + type: 'success', + }); + } } else if (key === 'credentials-open') { this.credentialOpenDialogVisible = true; } else if (key === 'credentials-new') { diff --git a/packages/editor-ui/src/components/WorkflowOpen.vue b/packages/editor-ui/src/components/WorkflowOpen.vue index abedea5b4c..45f1cfa0ff 100644 --- a/packages/editor-ui/src/components/WorkflowOpen.vue +++ b/packages/editor-ui/src/components/WorkflowOpen.vue @@ -33,6 +33,7 @@ import WorkflowActivator from '@/components/WorkflowActivator.vue'; import { restApi } from '@/components/mixins/restApi'; import { genericHelpers } from '@/components/mixins/genericHelpers'; +import { workflowHelpers } from '@/components/mixins/workflowHelpers'; import { showMessage } from '@/components/mixins/showMessage'; import { titleChange } from '@/components/mixins/titleChange'; import { IWorkflowShortResponse } from '@/Interface'; @@ -43,7 +44,7 @@ export default mixins( genericHelpers, restApi, showMessage, - titleChange, + workflowHelpers, ).extend({ name: 'WorkflowOpen', props: [ @@ -89,10 +90,20 @@ export default mixins( this.$emit('closeDialog'); return false; }, - 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') { - this.$titleSet(data.name, 'IDLE'); - this.$emit('openWorkflow', data.id); + const workflowId = this.$store.getters.workflowId; + const result = await this.dataHasChanged(workflowId); + 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'); + if (importConfirm === false) { + return; + } else { + this.$emit('openWorkflow', data.id); + } + } else { + this.$emit('openWorkflow', data.id); + } } }, openDialog () { diff --git a/packages/editor-ui/src/components/mixins/workflowHelpers.ts b/packages/editor-ui/src/components/mixins/workflowHelpers.ts index 507d6d31e4..b3c2416b28 100644 --- a/packages/editor-ui/src/components/mixins/workflowHelpers.ts +++ b/packages/editor-ui/src/components/mixins/workflowHelpers.ts @@ -22,6 +22,7 @@ import { INodeTypesMaxCount, INodeUi, IWorkflowData, + IWorkflowDb, IWorkflowDataUpdate, XYPositon, } from '../../Interface'; @@ -30,6 +31,8 @@ import { restApi } from '@/components/mixins/restApi'; import { nodeHelpers } from '@/components/mixins/nodeHelpers'; import { showMessage } from '@/components/mixins/showMessage'; +import { isEqual } from 'lodash'; + import mixins from 'vue-typed-mixins'; export const workflowHelpers = mixins( @@ -478,5 +481,29 @@ export const workflowHelpers = mixins( node.position[1] += offsetPosition[1]; } }, + async dataHasChanged(id: string) { + const currentData = await this.getWorkflowDataToSave(); + + let data: IWorkflowDb; + data = await this.restApi().getWorkflow(id); + + if(data !== undefined) { + const x = { + nodes: data.nodes, + connections: data.connections, + settings: data.settings, + name: data.name + }; + const y = { + nodes: currentData.nodes, + connections: currentData.connections, + settings: currentData.settings, + name: currentData.name + }; + return !isEqual(x, y); + } + + return true; + }, }, }); diff --git a/packages/editor-ui/src/views/NodeView.vue b/packages/editor-ui/src/views/NodeView.vue index dd4c756557..4b12a7d7bc 100644 --- a/packages/editor-ui/src/views/NodeView.vue +++ b/packages/editor-ui/src/views/NodeView.vue @@ -128,9 +128,7 @@ import RunData from '@/components/RunData.vue'; import mixins from 'vue-typed-mixins'; -import { v4 as uuidv4 } from 'uuid'; - -import { debounce } from 'lodash'; +import { debounce, isEqual } from 'lodash'; import axios from 'axios'; import { IConnection, @@ -191,6 +189,36 @@ export default mixins( // When a node gets set as active deactivate the create-menu this.createNodeActive = false; }, + nodes: { + async handler (val, oldVal) { + // Load a workflow + let workflowId = null as string | null; + if (this.$route && this.$route.params.name) { + workflowId = this.$route.params.name; + } + if(workflowId !== null) { + this.isDirty = await this.dataHasChanged(workflowId); + } else { + this.isDirty = true; + } + }, + deep: true + }, + connections: { + async handler (val, oldVal) { + // Load a workflow + let workflowId = null as string | null; + if (this.$route && this.$route.params.name) { + workflowId = this.$route.params.name; + } + if(workflowId !== null) { + this.isDirty = await this.dataHasChanged(workflowId); + } else { + this.isDirty = true; + } + }, + deep: true + }, }, computed: { activeNode (): INodeUi | null { @@ -264,6 +292,7 @@ export default mixins( ctrlKeyPressed: false, debouncedFunctions: [] as any[], // tslint:disable-line:no-any stopExecutionInProgress: false, + isDirty: false, }; }, beforeDestroy () { @@ -335,6 +364,8 @@ export default mixins( this.$store.commit('setWorkflowSettings', data.settings || {}); await this.addNodes(data.nodes, data.connections); + + return data; }, mouseDown (e: MouseEvent) { // Save the location of the mouse click @@ -436,6 +467,8 @@ export default mixins( e.stopPropagation(); e.preventDefault(); + this.isDirty = false; + this.callDebounced('saveCurrentWorkflow', 1000); } else if (e.key === 'Enter') { // Activate the last selected node @@ -1312,12 +1345,14 @@ export default mixins( if (this.$route.params.action === 'workflowSave') { // 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 + this.isDirty = false; return Promise.resolve(); } if (this.$route.name === 'ExecutionById') { // Load an execution const executionId = this.$route.params.id; + await this.openExecution(executionId); } else { // Load a workflow @@ -1325,7 +1360,6 @@ export default mixins( if (this.$route.params.name) { workflowId = this.$route.params.name; } - if (workflowId !== null) { const workflow = await this.restApi().getWorkflow(workflowId); this.$titleSet(workflow.name, 'IDLE'); @@ -1339,6 +1373,17 @@ export default mixins( document.addEventListener('keydown', this.keyDown); document.addEventListener('keyup', this.keyUp); + + window.addEventListener("beforeunload", (e) => { + if(this.isDirty === true) { + const confirmationMessage = 'It looks like you have been editing something. ' + + 'If you leave before saving, your changes will be lost.'; + (e || window.event).returnValue = confirmationMessage; //Gecko + IE + return confirmationMessage; //Gecko + Webkit, Safari, Chrome etc. + } else { + return; + } + }); }, __addConnection (connection: [IConnection, IConnection], addVisualConnection = false) { if (addVisualConnection === true) { @@ -1890,13 +1935,13 @@ export default mixins( async mounted () { this.$root.$on('importWorkflowData', async (data: IDataObject) => { - await this.importWorkflowData(data.data as IWorkflowDataUpdate); + const resData = await this.importWorkflowData(data.data as IWorkflowDataUpdate); }); this.$root.$on('importWorkflowUrl', async (data: IDataObject) => { const workflowData = await this.getWorkflowDataFromUrl(data.url as string); if (workflowData !== undefined) { - await this.importWorkflowData(workflowData); + const resData = await this.importWorkflowData(workflowData); } });