refactor(editor): Turn showMessage mixin to composable (#6081) (#6244)

* refactor(editor): Turn showMessage mixin to composable (#6081)

* refactor(editor): move $getExecutionError from showMessages mixin to pushConnection (it is used there only)

* refactor(editor): resolve showMessage mixin methods

* fix(editor): use composable instead of mixin

* fix(editor): resolve conflicts

* fix(editor): replace clearAllStickyNotifications

* fix(editor): replace confirmMessage

* fix(editor): replace confirmMessage

* fix(editor): replace confirmMessage

* fix(editor): remove last confirmMessage usage

* fix(editor): remove $prompt usage

* fix(editor): remove $show methods

* fix(editor): lint fix

* fix(editor): lint fix

* fix(editor): fixes after review

* fix(editor): Fix external hook call in App

* fix(editor): mixins & composables

* fix: add pushConnection setup composables to components as well

* fix(editor): mixins & composables

* fix(editor): mixins & composables

* fix: add void on non-await async calls

* fix: fix close without connecting confirmation

* fix: remove .only

---------

Co-authored-by: Alex Grozav <alex@grozav.com>
This commit is contained in:
Csaba Tuncsik
2023-05-15 18:41:13 +02:00
committed by GitHub
parent f1598d6fdc
commit 51fb913d37
80 changed files with 1126 additions and 978 deletions

View File

@@ -162,7 +162,7 @@
</template>
<script lang="ts">
import Vue from 'vue';
import Vue, { defineComponent } from 'vue';
import { mapStores } from 'pinia';
import type {
@@ -186,7 +186,7 @@ import {
MAIN_HEADER_TABS,
MODAL_CANCEL,
MODAL_CLOSE,
MODAL_CONFIRMED,
MODAL_CONFIRM,
NODE_OUTPUT_DEFAULT_KEY,
ONBOARDING_CALL_SIGNUP_MODAL_KEY,
ONBOARDING_PROMPT_TIMEBOX,
@@ -206,22 +206,22 @@ import { copyPaste } from '@/mixins/copyPaste';
import { externalHooks } from '@/mixins/externalHooks';
import { genericHelpers } from '@/mixins/genericHelpers';
import { moveNodeWorkflow } from '@/mixins/moveNodeWorkflow';
import useGlobalLinkActions from '@/composables/useGlobalLinkActions';
import useCanvasMouseSelect from '@/composables/useCanvasMouseSelect';
import { showMessage } from '@/mixins/showMessage';
import { useTitleChange } from '@/composables/useTitleChange';
import {
useGlobalLinkActions,
useCanvasMouseSelect,
useMessage,
useToast,
useTitleChange,
} from '@/composables';
import { useUniqueNodeName } from '@/composables/useUniqueNodeName';
import { useI18n } from '@/composables/useI18n';
import { workflowHelpers } from '@/mixins/workflowHelpers';
import { workflowRun } from '@/mixins/workflowRun';
import NodeDetailsView from '@/components/NodeDetailsView.vue';
import Node from '@/components/Node.vue';
import NodeSettings from '@/components/NodeSettings.vue';
import Sticky from '@/components/Sticky.vue';
import CanvasAddButton from './CanvasAddButton.vue';
import mixins from 'vue-typed-mixins';
import { v4 as uuid } from 'uuid';
import type {
IConnection,
@@ -313,38 +313,38 @@ interface AddNodeOptions {
dragAndDrop?: boolean;
}
const NodeCreator = async () => import('@/components/Node/NodeCreator/NodeCreator.vue');
const NodeCreation = async () => import('@/components/Node/NodeCreation.vue');
const CanvasControls = async () => import('@/components/CanvasControls.vue');
export default mixins(
copyPaste,
externalHooks,
genericHelpers,
moveNodeWorkflow,
showMessage,
workflowHelpers,
workflowRun,
debounceHelper,
).extend({
export default defineComponent({
name: 'NodeView',
mixins: [
copyPaste,
externalHooks,
genericHelpers,
moveNodeWorkflow,
workflowHelpers,
workflowRun,
debounceHelper,
],
components: {
NodeDetailsView,
Node,
NodeCreator,
NodeSettings,
Sticky,
CanvasAddButton,
NodeCreation,
CanvasControls,
},
setup() {
setup(props) {
return {
...useCanvasMouseSelect(),
...useGlobalLinkActions(),
...useTitleChange(),
...useToast(),
...useMessage(),
...useUniqueNodeName(),
...useI18n(),
...workflowRun.setup?.(props),
};
},
errorCaptured: (err, vm, info) => {
@@ -415,15 +415,21 @@ export default mixins(
return;
}
if (this.uiStore.stateIsDirty) {
const confirmModal = await this.confirmModal(
const confirmModal = await this.confirm(
this.$locale.baseText('generic.unsavedWork.confirmMessage.message'),
this.$locale.baseText('generic.unsavedWork.confirmMessage.headline'),
'warning',
this.$locale.baseText('generic.unsavedWork.confirmMessage.confirmButtonText'),
this.$locale.baseText('generic.unsavedWork.confirmMessage.cancelButtonText'),
true,
{
title: this.$locale.baseText('generic.unsavedWork.confirmMessage.headline'),
type: 'warning',
confirmButtonText: this.$locale.baseText(
'generic.unsavedWork.confirmMessage.confirmButtonText',
),
cancelButtonText: this.$locale.baseText(
'generic.unsavedWork.confirmMessage.cancelButtonText',
),
showClose: true,
},
);
if (confirmModal === MODAL_CONFIRMED) {
if (confirmModal === MODAL_CONFIRM) {
// Make sure workflow id is empty when leaving the editor
this.workflowsStore.setWorkflowId(PLACEHOLDER_EMPTY_WORKFLOW_ID);
const saved = await this.saveCurrentWorkflow({}, false);
@@ -631,11 +637,11 @@ export default mixins(
source: 'canvas',
};
this.$telemetry.track('User clicked execute node button', telemetryPayload);
this.$externalHooks().run('nodeView.onRunNode', telemetryPayload);
this.runWorkflow(nodeName, source);
void this.$externalHooks().run('nodeView.onRunNode', telemetryPayload);
void this.runWorkflow(nodeName, source);
},
async onRunWorkflow() {
this.getWorkflowDataToSave().then((workflowData) => {
void this.getWorkflowDataToSave().then((workflowData) => {
const telemetryPayload = {
workflow_id: this.workflowsStore.workflowId,
node_graph_string: JSON.stringify(
@@ -644,7 +650,7 @@ export default mixins(
),
};
this.$telemetry.track('User clicked execute workflow button', telemetryPayload);
this.$externalHooks().run('nodeView.onRunWorkflow', telemetryPayload);
void this.$externalHooks().run('nodeView.onRunWorkflow', telemetryPayload);
});
await this.runWorkflow();
@@ -660,7 +666,7 @@ export default mixins(
this.registerCustomAction('showNodeCreator', () =>
this.showTriggerCreator(NODE_CREATOR_OPEN_SOURCES.NO_TRIGGER_EXECUTION_TOOLTIP),
);
const notice = this.$showMessage({
const notice = this.showMessage({
type: 'info',
title: this.$locale.baseText('nodeView.cantExecuteNoTrigger'),
message,
@@ -688,7 +694,7 @@ export default mixins(
saved = true;
}
if (saved) {
this.$showMessage({
this.showMessage({
title: this.$locale.baseText('generic.workflowSaved'),
type: 'success',
});
@@ -708,7 +714,7 @@ export default mixins(
try {
data = await this.workflowsStore.getExecution(executionId);
} catch (error) {
this.$showError(error, this.$locale.baseText('nodeView.showError.openExecution.title'));
this.showError(error, this.$locale.baseText('nodeView.showError.openExecution.title'));
return;
}
if (data === undefined) {
@@ -732,7 +738,7 @@ export default mixins(
this.canvasStore.zoomToFit();
this.uiStore.stateIsDirty = false;
});
this.$externalHooks().run('execution.open', {
void this.$externalHooks().run('execution.open', {
workflowId: data.workflowData.id,
workflowName: data.workflowData.name,
executionId,
@@ -766,7 +772,7 @@ export default mixins(
}
}
if ((data as IExecutionsSummary).waitTill) {
this.$showMessage({
this.showMessage({
title: this.$locale.baseText('nodeView.thisExecutionHasntFinishedYet'),
message: `<a data-action="reload">${this.$locale.baseText(
'nodeView.refresh',
@@ -808,7 +814,7 @@ export default mixins(
let data: IWorkflowTemplate | undefined;
try {
this.$externalHooks().run('template.requested', { templateId });
void this.$externalHooks().run('template.requested', { templateId });
data = await this.templatesStore.getWorkflowTemplate(templateId);
if (!data) {
@@ -819,7 +825,7 @@ export default mixins(
);
}
} catch (error) {
this.$showError(error, this.$locale.baseText('nodeView.couldntImportWorkflow'));
this.showError(error, this.$locale.baseText('nodeView.couldntImportWorkflow'));
void this.$router.replace({ name: VIEWS.NEW_WORKFLOW });
return;
}
@@ -836,7 +842,7 @@ export default mixins(
this.uiStore.stateIsDirty = true;
});
this.$externalHooks().run('template.open', {
void this.$externalHooks().run('template.open', {
templateId,
templateName: data.name,
workflow: data.workflow,
@@ -887,7 +893,7 @@ export default mixins(
this.uiStore.stateIsDirty = false;
}
this.canvasStore.zoomToFit();
this.$externalHooks().run('workflow.open', {
void this.$externalHooks().run('workflow.open', {
workflowId: workflow.id,
workflowName: workflow.name,
});
@@ -941,7 +947,7 @@ export default mixins(
return;
}
this.callDebounced('onSaveKeyboardShortcut', { debounceTime: 1000 }, e);
void this.callDebounced('onSaveKeyboardShortcut', { debounceTime: 1000 }, e);
return;
}
@@ -969,7 +975,7 @@ export default mixins(
if (e.key === 'Escape') {
this.createNodeActive = false;
if (this.activeNode) {
this.$externalHooks().run('dataDisplay.nodeEditingFinished');
void this.$externalHooks().run('dataDisplay.nodeEditingFinished');
this.ndvStore.activeNodeName = null;
}
@@ -982,12 +988,12 @@ export default mixins(
}
if (e.key === 'd') {
this.callDebounced('deactivateSelectedNode', { debounceTime: 350 });
void this.callDebounced('deactivateSelectedNode', { debounceTime: 350 });
} else if (e.key === 'Delete' || e.key === 'Backspace') {
e.stopPropagation();
e.preventDefault();
this.callDebounced('deleteSelectedNodes', { debounceTime: 500 });
void this.callDebounced('deleteSelectedNodes', { debounceTime: 500 });
} else if (e.key === 'Tab') {
this.onToggleNodeCreator({
source: NODE_CREATOR_OPEN_SOURCES.TAB,
@@ -1000,22 +1006,26 @@ export default mixins(
} else if (e.key === 'F2' && !this.isReadOnly) {
const lastSelectedNode = this.lastSelectedNode;
if (lastSelectedNode !== null && lastSelectedNode.type !== STICKY_NODE_TYPE) {
this.callDebounced('renameNodePrompt', { debounceTime: 1500 }, lastSelectedNode.name);
void this.callDebounced(
'renameNodePrompt',
{ debounceTime: 1500 },
lastSelectedNode.name,
);
}
} else if (e.key === 'a' && this.isCtrlKeyPressed(e) === true) {
// Select all nodes
e.stopPropagation();
e.preventDefault();
this.callDebounced('selectAllNodes', { debounceTime: 1000 });
void this.callDebounced('selectAllNodes', { debounceTime: 1000 });
} else if (e.key === 'c' && this.isCtrlKeyPressed(e)) {
this.callDebounced('copySelectedNodes', { debounceTime: 1000 });
void this.callDebounced('copySelectedNodes', { debounceTime: 1000 });
} else if (e.key === 'x' && this.isCtrlKeyPressed(e)) {
// Cut nodes
e.stopPropagation();
e.preventDefault();
this.callDebounced('cutSelectedNodes', { debounceTime: 1000 });
void this.callDebounced('cutSelectedNodes', { debounceTime: 1000 });
} else if (e.key === 'n' && this.isCtrlKeyPressed(e) && e.altKey) {
// Create a new workflow
e.stopPropagation();
@@ -1030,7 +1040,7 @@ export default mixins(
void this.$router.push({ name: VIEWS.NEW_WORKFLOW });
}
this.$showMessage({
this.showMessage({
title: this.$locale.baseText('nodeView.showMessage.keyDown.title'),
type: 'success',
});
@@ -1049,7 +1059,7 @@ export default mixins(
e.stopPropagation();
e.preventDefault();
this.callDebounced('selectDownstreamNodes', { debounceTime: 1000 });
void this.callDebounced('selectDownstreamNodes', { debounceTime: 1000 });
} else if (e.key === 'ArrowRight') {
// Set child node active
const lastSelectedNode = this.lastSelectedNode;
@@ -1065,7 +1075,7 @@ export default mixins(
return;
}
this.callDebounced(
void this.callDebounced(
'nodeSelectedByName',
{ debounceTime: 100 },
connections.main[0][0].node,
@@ -1077,7 +1087,7 @@ export default mixins(
e.stopPropagation();
e.preventDefault();
this.callDebounced('selectUpstreamNodes', { debounceTime: 1000 });
void this.callDebounced('selectUpstreamNodes', { debounceTime: 1000 });
} else if (e.key === 'ArrowLeft') {
// Set parent node active
const lastSelectedNode = this.lastSelectedNode;
@@ -1097,7 +1107,7 @@ export default mixins(
return;
}
this.callDebounced(
void this.callDebounced(
'nodeSelectedByName',
{ debounceTime: 100 },
connections.main[0][0].node,
@@ -1169,7 +1179,7 @@ export default mixins(
}
if (nextSelectNode !== null) {
this.callDebounced(
void this.callDebounced(
'nodeSelectedByName',
{ debounceTime: 100 },
nextSelectNode,
@@ -1305,7 +1315,7 @@ export default mixins(
this.copyToClipboard(nodeData);
if (data.nodes.length > 0) {
if (!isCut) {
this.$showMessage({
this.showMessage({
title: 'Copied!',
message: '',
type: 'success',
@@ -1327,7 +1337,7 @@ export default mixins(
try {
this.stopExecutionInProgress = true;
await this.workflowsStore.stopCurrentExecution(executionId);
this.$showMessage({
this.showMessage({
title: this.$locale.baseText('nodeView.showMessage.stopExecutionTry.title'),
type: 'success',
});
@@ -1346,7 +1356,7 @@ export default mixins(
this.uiStore.removeActiveAction('workflowRunning');
this.titleSet(this.workflowsStore.workflowName, 'IDLE');
this.$showMessage({
this.showMessage({
title: this.$locale.baseText('nodeView.showMessage.stopExecutionCatch.unsaved.title'),
message: this.$locale.baseText(
'nodeView.showMessage.stopExecutionCatch.unsaved.message',
@@ -1373,18 +1383,18 @@ export default mixins(
this.workflowsStore.executingNode = null;
this.workflowsStore.setWorkflowExecutionData(executedData as IExecutionResponse);
this.uiStore.removeActiveAction('workflowRunning');
this.$showMessage({
this.showMessage({
title: this.$locale.baseText('nodeView.showMessage.stopExecutionCatch.title'),
message: this.$locale.baseText('nodeView.showMessage.stopExecutionCatch.message'),
type: 'success',
});
} else {
this.$showError(error, this.$locale.baseText('nodeView.showError.stopExecution.title'));
this.showError(error, this.$locale.baseText('nodeView.showError.stopExecution.title'));
}
}
this.stopExecutionInProgress = false;
this.getWorkflowDataToSave().then((workflowData) => {
void this.getWorkflowDataToSave().then((workflowData) => {
const trackProps = {
workflow_id: this.workflowsStore.workflowId,
node_graph_string: JSON.stringify(
@@ -1401,7 +1411,7 @@ export default mixins(
try {
await this.workflowsStore.removeTestWebhook(this.workflowsStore.workflowId);
} catch (error) {
this.$showError(
this.showError(
error,
this.$locale.baseText('nodeView.showError.stopWaitingForWebhook.title'),
);
@@ -1426,19 +1436,23 @@ export default mixins(
return;
}
const importConfirm = await this.confirmMessage(
const importConfirm = await this.confirm(
this.$locale.baseText('nodeView.confirmMessage.receivedCopyPasteData.message', {
interpolate: { plainTextData },
}),
this.$locale.baseText('nodeView.confirmMessage.receivedCopyPasteData.headline'),
'warning',
this.$locale.baseText(
'nodeView.confirmMessage.receivedCopyPasteData.confirmButtonText',
),
this.$locale.baseText('nodeView.confirmMessage.receivedCopyPasteData.cancelButtonText'),
{
type: 'warning',
confirmButtonText: this.$locale.baseText(
'nodeView.confirmMessage.receivedCopyPasteData.confirmButtonText',
),
cancelButtonText: this.$locale.baseText(
'nodeView.confirmMessage.receivedCopyPasteData.cancelButtonText',
),
},
);
if (!importConfirm) {
if (importConfirm !== MODAL_CONFIRM) {
return;
}
@@ -1475,7 +1489,7 @@ export default mixins(
workflowData = await this.workflowsStore.getWorkflowFromUrl(url);
} catch (error) {
this.stopLoading();
this.$showError(
this.showError(
error,
this.$locale.baseText('nodeView.showError.getWorkflowDataFromUrl.title'),
);
@@ -1593,10 +1607,7 @@ export default mixins(
this.workflowsStore.addWorkflowTagIds(tagIds);
}
} catch (error) {
this.$showError(
error,
this.$locale.baseText('nodeView.showError.importWorkflowData.title'),
);
this.showError(error, this.$locale.baseText('nodeView.showError.importWorkflowData.title'));
}
},
onDragOver(event: DragEvent) {
@@ -1660,7 +1671,7 @@ export default mixins(
},
showMaxNodeTypeError(nodeTypeData: INodeTypeDescription) {
const maxNodes = nodeTypeData.maxNodes;
this.$showMessage({
this.showMessage({
title: this.$locale.baseText('nodeView.showMessage.showMaxNodeTypeError.title'),
message: this.$locale.baseText('nodeView.showMessage.showMaxNodeTypeError.message', {
adjustToNumber: maxNodes,
@@ -1770,7 +1781,7 @@ export default mixins(
this.nodeTypesStore.getNodeType(nodeTypeName);
if (nodeTypeData === null) {
this.$showMessage({
this.showMessage({
title: this.$locale.baseText('nodeView.showMessage.addNodeButton.title'),
message: this.$locale.baseText('nodeView.showMessage.addNodeButton.message', {
interpolate: { nodeTypeName },
@@ -1881,7 +1892,7 @@ export default mixins(
workflow_id: this.workflowsStore.workflowId,
});
} else {
this.$externalHooks().run('nodeView.addNodeButton', { nodeTypeName });
void this.$externalHooks().run('nodeView.addNodeButton', { nodeTypeName });
useSegment().trackAddedTrigger(nodeTypeName);
const trackProperties: ITelemetryTrackProperties = {
node_type: nodeTypeName,
@@ -2490,15 +2501,21 @@ export default mixins(
} else {
const result = this.uiStore.stateIsDirty;
if (result) {
const confirmModal = await this.confirmModal(
const confirmModal = await this.confirm(
this.$locale.baseText('generic.unsavedWork.confirmMessage.message'),
this.$locale.baseText('generic.unsavedWork.confirmMessage.headline'),
'warning',
this.$locale.baseText('generic.unsavedWork.confirmMessage.confirmButtonText'),
this.$locale.baseText('generic.unsavedWork.confirmMessage.cancelButtonText'),
true,
{
title: this.$locale.baseText('generic.unsavedWork.confirmMessage.headline'),
type: 'warning',
confirmButtonText: this.$locale.baseText(
'generic.unsavedWork.confirmMessage.confirmButtonText',
),
cancelButtonText: this.$locale.baseText(
'generic.unsavedWork.confirmMessage.cancelButtonText',
),
showClose: true,
},
);
if (confirmModal === MODAL_CONFIRMED) {
if (confirmModal === MODAL_CONFIRM) {
const saved = await this.saveCurrentWorkflow();
if (saved) await this.settingsStore.fetchPromptsData();
} else if (confirmModal === MODAL_CLOSE) {
@@ -2515,7 +2532,7 @@ export default mixins(
try {
workflow = await this.workflowsStore.fetchWorkflow(workflowId);
} catch (error) {
this.$showError(error, this.$locale.baseText('openWorkflow.workflowNotFoundError'));
this.showError(error, this.$locale.baseText('openWorkflow.workflowNotFoundError'));
void this.$router.push({
name: VIEWS.NEW_WORKFLOW,
@@ -2921,7 +2938,7 @@ export default mixins(
is_welcome_note: node.name === QUICKSTART_NOTE_NAME,
});
} else {
this.$externalHooks().run('node.deleteNode', { node });
void this.$externalHooks().run('node.deleteNode', { node });
this.$telemetry.track('User deleted node', {
node_type: node.type,
workflow_id: this.workflowsStore.workflowId,
@@ -2999,7 +3016,7 @@ export default mixins(
},
async renameNodePrompt(currentName: string) {
try {
const promptResponsePromise = this.$prompt(
const promptResponsePromise = this.prompt(
this.$locale.baseText('nodeView.prompt.newName') + ':',
this.$locale.baseText('nodeView.prompt.renameNode') + `: ${currentName}`,
{
@@ -3557,7 +3574,7 @@ export default mixins(
'*',
);
}
this.$showMessage({
this.showMessage({
title: this.$locale.baseText('openWorkflow.workflowImportError'),
message: (e as Error).message,
type: 'error',
@@ -3581,7 +3598,7 @@ export default mixins(
'*',
);
}
this.$showMessage({
this.showMessage({
title: this.$locale.baseText('nodeView.showError.openExecution.title'),
message: (e as Error).message,
type: 'error',
@@ -3656,7 +3673,7 @@ export default mixins(
this.nodeCreatorStore.selectedView === TRIGGER_NODE_CREATOR_VIEW ? 'trigger' : 'regular';
if (createNodeActive === true) this.nodeCreatorStore.setOpenSource(source);
this.$externalHooks().run('nodeView.createNodeActiveChanged', {
void this.$externalHooks().run('nodeView.createNodeActiveChanged', {
source,
mode,
createNodeActive,
@@ -3780,7 +3797,7 @@ export default mixins(
try {
await Promise.all(loadPromises);
} catch (error) {
this.$showError(
this.showError(
error,
this.$locale.baseText('nodeView.showError.mounted1.title'),
this.$locale.baseText('nodeView.showError.mounted1.message') + ':',
@@ -3800,7 +3817,7 @@ export default mixins(
);
}
} catch (error) {
this.$showError(
this.showError(
error,
this.$locale.baseText('nodeView.showError.mounted2.title'),
this.$locale.baseText('nodeView.showError.mounted2.message') + ':',
@@ -3815,7 +3832,7 @@ export default mixins(
});
// TODO: This currently breaks since front-end hooks are still not updated to work with pinia store
this.$externalHooks()
void this.$externalHooks()
.run('nodeView.mount')
.catch((e) => {});
@@ -3831,7 +3848,7 @@ export default mixins(
if (onboardingResponse.title && onboardingResponse.description) {
setTimeout(async () => {
this.$showToast({
this.showToast({
type: 'info',
title: onboardingResponse.title,
message: onboardingResponse.description,