Revert "refactor(editor): Turn showMessage mixin to composable" (#6243)

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

This reverts commit b95fcd7323.
This commit is contained in:
Csaba Tuncsik
2023-05-12 16:43:34 +02:00
committed by GitHub
parent 13bcec1661
commit 638e3f209d
75 changed files with 863 additions and 991 deletions

View File

@@ -1,15 +1,10 @@
import { defineComponent } from 'vue';
import mixins from 'vue-typed-mixins';
import dateformat from 'dateformat';
import { VIEWS } from '@/constants';
import { useToast } from '@/composables';
import { showMessage } from '@/mixins/showMessage';
export const genericHelpers = defineComponent({
setup() {
return {
...useToast(),
};
},
export const genericHelpers = mixins(showMessage).extend({
data() {
return {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -51,7 +46,7 @@ export const genericHelpers = defineComponent({
},
editAllowedCheck(): boolean {
if (this.isReadOnly) {
this.showMessage({
this.$showMessage({
// title: 'Workflow can not be changed!',
title: this.$locale.baseText('genericHelpers.showMessage.title'),
message: this.$locale.baseText('genericHelpers.showMessage.message'),

View File

@@ -1,16 +1,11 @@
import { defineComponent } from 'vue';
import { useToast } from '@/composables';
import mixins from 'vue-typed-mixins';
import { showMessage } from './showMessage';
import { VERSIONS_MODAL_KEY } from '@/constants';
import { mapStores } from 'pinia';
import { useUIStore } from '@/stores/ui.store';
import { useVersionsStore } from '@/stores/versions.store';
export const newVersions = defineComponent({
setup() {
return {
...useToast(),
};
},
export const newVersions = mixins(showMessage).extend({
computed: {
...mapStores(useUIStore, useVersionsStore),
},
@@ -33,7 +28,7 @@ export const newVersions = defineComponent({
}
message = `${message} <a class="primary-color">More info</a>`;
this.showToast({
this.$showToast({
title: 'Critical update available',
message,
onClick: () => {

View File

@@ -5,7 +5,6 @@ import { stringSizeInBytes } from '@/utils';
import { MAX_WORKFLOW_PINNED_DATA_SIZE, PIN_DATA_NODE_TYPES_DENYLIST } from '@/constants';
import { mapStores } from 'pinia';
import { useWorkflowsStore } from '@/stores/workflows.store';
import { useToast } from '@/composables';
export interface IPinDataContext {
node: INodeUi;
@@ -14,11 +13,6 @@ export interface IPinDataContext {
}
export const pinData = (Vue as Vue.VueConstructor<Vue & IPinDataContext>).extend({
setup() {
return {
...useToast(),
};
},
computed: {
...mapStores(useWorkflowsStore),
pinData(): IPinData[string] | undefined {
@@ -78,7 +72,7 @@ export const pinData = (Vue as Vue.VueConstructor<Vue & IPinDataContext>).extend
})} ${error.message}`;
}
this.showError(error, title);
this.$showError(error, title);
return false;
}
@@ -90,7 +84,7 @@ export const pinData = (Vue as Vue.VueConstructor<Vue & IPinDataContext>).extend
this.workflowsStore.pinDataSize + stringSizeInBytes(data) >
MAX_WORKFLOW_PINNED_DATA_SIZE
) {
this.showError(
this.$showError(
new Error(this.$locale.baseText('ndv.pinData.error.tooLarge.description')),
this.$locale.baseText('ndv.pinData.error.tooLarge.title'),
);

View File

@@ -7,7 +7,8 @@ import type {
import { externalHooks } from '@/mixins/externalHooks';
import { nodeHelpers } from '@/mixins/nodeHelpers';
import { useTitleChange, useToast } from '@/composables';
import { showMessage } from '@/mixins/showMessage';
import { useTitleChange } from '@/composables/useTitleChange';
import { workflowHelpers } from '@/mixins/workflowHelpers';
import type {
@@ -18,7 +19,6 @@ import type {
IRunExecutionData,
IWorkflowBase,
SubworkflowOperationError,
IExecuteContextData,
} from 'n8n-workflow';
import { TelemetryHelpers } from 'n8n-workflow';
@@ -35,11 +35,15 @@ import { useSettingsStore } from '@/stores/settings.store';
import { parse } from 'flatted';
import { useSegment } from '@/stores/segment.store';
export const pushConnection = mixins(externalHooks, nodeHelpers, workflowHelpers).extend({
export const pushConnection = mixins(
externalHooks,
nodeHelpers,
showMessage,
workflowHelpers,
).extend({
setup() {
return {
...useTitleChange(),
...useToast(),
};
},
data() {
@@ -320,7 +324,7 @@ export const pushConnection = mixins(externalHooks, nodeHelpers, workflowHelpers
const runDataExecuted = pushData.data;
let runDataExecutedErrorMessage = this.getExecutionError(runDataExecuted.data);
let runDataExecutedErrorMessage = this.$getExecutionError(runDataExecuted.data);
if (pushData.data.status === 'crashed') {
runDataExecutedErrorMessage = this.$locale.baseText(
@@ -363,7 +367,7 @@ export const pushConnection = mixins(externalHooks, nodeHelpers, workflowHelpers
// Workflow did start but had been put to wait
this.titleSet(workflow.name as string, 'IDLE');
this.showToast({
this.$showToast({
title: 'Workflow started waiting',
message: `${action} <a href="https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.wait/" target="_blank">More info</a>`,
type: 'success',
@@ -418,7 +422,7 @@ export const pushConnection = mixins(externalHooks, nodeHelpers, workflowHelpers
this.workflowsStore.subWorkflowExecutionError = error;
this.showMessage({
this.$showMessage({
title: error.message,
message: error.description,
type: 'error',
@@ -432,7 +436,7 @@ export const pushConnection = mixins(externalHooks, nodeHelpers, workflowHelpers
title = 'Problem executing workflow';
}
this.showMessage({
this.$showMessage({
title,
message: runDataExecutedErrorMessage,
type: 'error',
@@ -455,7 +459,7 @@ export const pushConnection = mixins(externalHooks, nodeHelpers, workflowHelpers
execution.data.resultData.runData &&
execution.data.resultData.runData[execution.executedNode];
if (nodeType && nodeType.polling && !nodeOutput) {
this.showMessage({
this.$showMessage({
title: this.$locale.baseText('pushConnection.pollingNode.dataNotFound', {
interpolate: {
service: getTriggerNodeServiceName(nodeType),
@@ -469,13 +473,13 @@ export const pushConnection = mixins(externalHooks, nodeHelpers, workflowHelpers
type: 'success',
});
} else {
this.showMessage({
this.$showMessage({
title: this.$locale.baseText('pushConnection.nodeExecutedSuccessfully'),
type: 'success',
});
}
} else {
this.showMessage({
this.$showMessage({
title: this.$locale.baseText('pushConnection.workflowExecutedSuccessfully'),
type: 'success',
});
@@ -578,38 +582,5 @@ export const pushConnection = mixins(externalHooks, nodeHelpers, workflowHelpers
}
return true;
},
getExecutionError(data: IRunExecutionData | IExecuteContextData) {
const error = data.resultData.error;
let errorMessage: string;
if (data.resultData.lastNodeExecuted && error) {
errorMessage = error.message || error.description;
} else {
errorMessage = this.$locale.baseText('pushConnection.executionError', {
interpolate: { error: '!' },
});
if (error && error.message) {
let nodeName: string | undefined;
if ('node' in error) {
nodeName = typeof error.node === 'string' ? error.node : error.node!.name;
}
const receivedError = nodeName ? `${nodeName}: ${error.message}` : error.message;
errorMessage = this.$locale.baseText('pushConnection.executionError', {
interpolate: {
error: `.${this.$locale.baseText('pushConnection.executionError.details', {
interpolate: {
details: receivedError,
},
})}`,
},
});
}
}
return errorMessage;
},
},
});

View File

@@ -0,0 +1,235 @@
// @ts-ignore
import type { ElNotificationComponent, ElNotificationOptions } from 'element-ui/types/notification';
import mixins from 'vue-typed-mixins';
import { externalHooks } from '@/mixins/externalHooks';
import type { IExecuteContextData, IRunExecutionData } from 'n8n-workflow';
import type { ElMessageBoxOptions } from 'element-ui/types/message-box';
import type { ElMessageComponent, ElMessageOptions, MessageType } from 'element-ui/types/message';
import { sanitizeHtml } from '@/utils';
import { mapStores } from 'pinia';
import { useWorkflowsStore } from '@/stores/workflows.store';
let stickyNotificationQueue: ElNotificationComponent[] = [];
export const showMessage = mixins(externalHooks).extend({
computed: {
...mapStores(useWorkflowsStore),
},
methods: {
$showMessage(
messageData: Omit<ElNotificationOptions, 'message'> & { message?: string },
track = true,
) {
messageData.dangerouslyUseHTMLString = true;
messageData.message = messageData.message
? sanitizeHtml(messageData.message)
: messageData.message;
if (messageData.position === undefined) {
messageData.position = 'bottom-right';
}
const notification = this.$notify(messageData as ElNotificationOptions);
if (messageData.duration === 0) {
stickyNotificationQueue.push(notification);
}
if (messageData.type === 'error' && track) {
this.$telemetry.track('Instance FE emitted error', {
error_title: messageData.title,
error_message: messageData.message,
caused_by_credential: this.causedByCredential(messageData.message),
workflow_id: this.workflowsStore.workflowId,
});
}
return notification;
},
$showToast(config: {
title: string;
message: string;
onClick?: () => void;
onClose?: () => void;
duration?: number;
customClass?: string;
closeOnClick?: boolean;
type?: MessageType;
}) {
// eslint-disable-next-line prefer-const
let notification: ElNotificationComponent;
if (config.closeOnClick) {
const cb = config.onClick;
config.onClick = () => {
if (notification) {
notification.close();
}
if (cb) {
cb();
}
};
}
notification = this.$showMessage({
title: config.title,
message: config.message,
onClick: config.onClick,
onClose: config.onClose,
duration: config.duration,
customClass: config.customClass,
type: config.type,
});
return notification;
},
$showAlert(config: ElMessageOptions): ElMessageComponent {
return this.$message(config);
},
$getExecutionError(data: IRunExecutionData | IExecuteContextData) {
const error = data.resultData.error;
let errorMessage: string;
if (data.resultData.lastNodeExecuted && error) {
errorMessage = error.message || error.description;
} else {
errorMessage = 'There was a problem executing the workflow!';
if (error && error.message) {
let nodeName: string | undefined;
if ('node' in error) {
nodeName = typeof error.node === 'string' ? error.node : error.node!.name;
}
const receivedError = nodeName ? `${nodeName}: ${error.message}` : error.message;
errorMessage = `There was a problem executing the workflow:<br /><strong>"${receivedError}"</strong>`;
}
}
return errorMessage;
},
$showError(e: Error | unknown, title: string, message?: string) {
const error = e as Error;
const messageLine = message ? `${message}<br/>` : '';
this.$showMessage(
{
title,
message: `
${messageLine}
<i>${error.message}</i>
${this.collapsableDetails(error)}`,
type: 'error',
duration: 0,
},
false,
);
void this.$externalHooks().run('showMessage.showError', {
title,
message,
errorMessage: error.message,
});
this.$telemetry.track('Instance FE emitted error', {
error_title: title,
error_description: message,
error_message: error.message,
caused_by_credential: this.causedByCredential(error.message),
workflow_id: this.workflowsStore.workflowId,
});
},
async confirmMessage(
message: string,
headline: string,
type: MessageType | null = 'warning',
confirmButtonText?: string,
cancelButtonText?: string,
): Promise<boolean> {
try {
const options: ElMessageBoxOptions = {
confirmButtonText: confirmButtonText || this.$locale.baseText('showMessage.ok'),
cancelButtonText: cancelButtonText || this.$locale.baseText('showMessage.cancel'),
dangerouslyUseHTMLString: true,
...(type && { type }),
};
const sanitizedMessage = sanitizeHtml(message);
await this.$confirm(sanitizedMessage, headline, options);
return true;
} catch (e) {
return false;
}
},
async confirmModal(
message: string,
headline: string,
type: MessageType | null = 'warning',
confirmButtonText?: string,
cancelButtonText?: string,
showClose = false,
): Promise<string> {
try {
const options: ElMessageBoxOptions = {
confirmButtonText: confirmButtonText || this.$locale.baseText('showMessage.ok'),
cancelButtonText: cancelButtonText || this.$locale.baseText('showMessage.cancel'),
dangerouslyUseHTMLString: true,
showClose,
...(type && { type }),
};
const sanitizedMessage = sanitizeHtml(message);
await this.$confirm(sanitizedMessage, headline, options);
return 'confirmed';
} catch (e) {
return e as string;
}
},
clearAllStickyNotifications() {
stickyNotificationQueue.map((notification: ElNotificationComponent) => {
if (notification) {
notification.close();
}
});
stickyNotificationQueue = [];
},
// @ts-ignore
collapsableDetails({ description, node }: Error) {
if (!description) return '';
const errorDescription =
description.length > 500 ? `${description.slice(0, 500)}...` : description;
return `
<br>
<br>
<details>
<summary
style="color: #ff6d5a; font-weight: bold; cursor: pointer;"
>
${this.$locale.baseText('showMessage.showDetails')}
</summary>
<p>${node.name}: ${errorDescription}</p>
</details>
`;
},
/**
* Whether a workflow execution error was caused by a credential issue, as reflected by the error message.
*/
causedByCredential(message: string | undefined) {
if (!message) return false;
return message.includes('Credentials for') && message.includes('are not set');
},
},
});

View File

@@ -1,6 +1,6 @@
import { externalHooks } from '@/mixins/externalHooks';
import { workflowHelpers } from '@/mixins/workflowHelpers';
import { useToast } from '@/composables';
import { showMessage } from '@/mixins/showMessage';
import mixins from 'vue-typed-mixins';
import {
@@ -13,12 +13,7 @@ import { useUIStore } from '@/stores/ui.store';
import { useSettingsStore } from '@/stores/settings.store';
import { useWorkflowsStore } from '@/stores/workflows.store';
export const workflowActivate = mixins(externalHooks, workflowHelpers).extend({
setup() {
return {
...useToast(),
};
},
export const workflowActivate = mixins(externalHooks, workflowHelpers, showMessage).extend({
data() {
return {
updatingWorkflowActivation: false,
@@ -65,7 +60,7 @@ export const workflowActivate = mixins(externalHooks, workflowHelpers).extend({
try {
if (isWorkflowActive && newActiveState) {
this.showMessage({
this.$showMessage({
title: this.$locale.baseText('workflowActivator.workflowIsActive'),
type: 'success',
});
@@ -75,7 +70,7 @@ export const workflowActivate = mixins(externalHooks, workflowHelpers).extend({
}
if (isCurrentWorkflow && nodesIssuesExist && newActiveState === true) {
this.showMessage({
this.$showMessage({
title: this.$locale.baseText(
'workflowActivator.showMessage.activeChangedNodesIssuesExistTrue.title',
),
@@ -92,7 +87,7 @@ export const workflowActivate = mixins(externalHooks, workflowHelpers).extend({
await this.updateWorkflow({ workflowId: currWorkflowId, active: newActiveState });
} catch (error) {
const newStateName = newActiveState === true ? 'activated' : 'deactivated';
this.showError(
this.$showError(
error,
this.$locale.baseText('workflowActivator.showError.title', {
interpolate: { newStateName },

View File

@@ -4,7 +4,6 @@ import {
WEBHOOK_NODE_TYPE,
VIEWS,
EnterpriseEditionFeature,
MODAL_CONFIRM,
} from '@/constants';
import type {
@@ -41,7 +40,7 @@ import type {
import { externalHooks } from '@/mixins/externalHooks';
import { nodeHelpers } from '@/mixins/nodeHelpers';
import { useToast, useMessage } from '@/composables';
import { showMessage } from '@/mixins/showMessage';
import { isEqual } from 'lodash-es';
@@ -321,13 +320,7 @@ function executeData(
return executeData;
}
export const workflowHelpers = mixins(externalHooks, nodeHelpers).extend({
setup() {
return {
...useToast(),
...useMessage(),
};
},
export const workflowHelpers = mixins(externalHooks, nodeHelpers, showMessage).extend({
computed: {
...mapStores(
useNodeTypesStore,
@@ -748,31 +741,26 @@ export const workflowHelpers = mixins(externalHooks, nodeHelpers).extend({
params: { name: currentWorkflow },
}).href;
const overwrite = await this.confirm(
const overwrite = await this.confirmMessage(
this.$locale.baseText('workflows.concurrentChanges.confirmMessage.message', {
interpolate: {
url,
},
}),
this.$locale.baseText('workflows.concurrentChanges.confirmMessage.title'),
{
confirmButtonText: this.$locale.baseText(
'workflows.concurrentChanges.confirmMessage.confirmButtonText',
),
cancelButtonText: this.$locale.baseText(
'workflows.concurrentChanges.confirmMessage.cancelButtonText',
),
},
null,
this.$locale.baseText('workflows.concurrentChanges.confirmMessage.confirmButtonText'),
this.$locale.baseText('workflows.concurrentChanges.confirmMessage.cancelButtonText'),
);
if (overwrite === MODAL_CONFIRM) {
if (overwrite) {
return this.saveCurrentWorkflow({ id, name, tags }, redirect, true);
}
return false;
}
this.showMessage({
this.$showMessage({
title: this.$locale.baseText('workflowHelpers.showMessage.title'),
message: error.message,
type: 'error',
@@ -902,7 +890,7 @@ export const workflowHelpers = mixins(externalHooks, nodeHelpers).extend({
} catch (e) {
this.uiStore.removeActiveAction('workflowSaving');
this.showMessage({
this.$showMessage({
title: this.$locale.baseText('workflowHelpers.showMessage.title'),
message: (e as Error).message,
type: 'error',

View File

@@ -5,7 +5,7 @@ import { NodeHelpers, TelemetryHelpers } from 'n8n-workflow';
import { externalHooks } from '@/mixins/externalHooks';
import { workflowHelpers } from '@/mixins/workflowHelpers';
import { useToast } from '@/composables';
import { showMessage } from '@/mixins/showMessage';
import mixins from 'vue-typed-mixins';
import { useTitleChange } from '@/composables/useTitleChange';
@@ -14,11 +14,10 @@ import { useUIStore } from '@/stores/ui.store';
import { useWorkflowsStore } from '@/stores/workflows.store';
import { useRootStore } from '@/stores/n8nRoot.store';
export const workflowRun = mixins(externalHooks, workflowHelpers).extend({
export const workflowRun = mixins(externalHooks, workflowHelpers, showMessage).extend({
setup() {
return {
...useTitleChange(),
...useToast(),
};
},
computed: {
@@ -107,7 +106,7 @@ export const workflowRun = mixins(externalHooks, workflowHelpers).extend({
trackNodeIssues.push(trackNodeIssue);
}
this.showMessage({
this.$showMessage({
title: this.$locale.baseText('workflowRun.showMessage.title'),
message: errorMessages.join('<br />'),
type: 'error',
@@ -240,7 +239,7 @@ export const workflowRun = mixins(externalHooks, workflowHelpers).extend({
return runWorkflowApiResponse;
} catch (error) {
this.titleSet(workflow.name as string, 'ERROR');
this.showError(error, this.$locale.baseText('workflowRun.showError.title'));
this.$showError(error, this.$locale.baseText('workflowRun.showError.title'));
return undefined;
}
},