refactor(editor): Refactor utils files and mixins (#4654)
* ✨ Added `utils` module. Moved `canvasHelpers` and old `utils.ts` file to it * ✨ Moved rest of utils and helpers * ⚡ Fixing sytax errors * 🔨 Refactoring new utils files * 🔨 Organizing imports, adding comments and a bit more refactoring * ✔️ Fixing tests * 🔨 Moving mixins to `src`
This commit is contained in:
committed by
GitHub
parent
67983e8f94
commit
5059c57f4a
225
packages/editor-ui/src/mixins/showMessage.ts
Normal file
225
packages/editor-ui/src/mixins/showMessage.ts
Normal file
@@ -0,0 +1,225 @@
|
||||
// @ts-ignore
|
||||
import { ElNotificationComponent, ElNotificationOptions } from 'element-ui/types/notification';
|
||||
import mixins from 'vue-typed-mixins';
|
||||
|
||||
import { externalHooks } from '@/mixins/externalHooks';
|
||||
import {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';
|
||||
|
||||
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);
|
||||
|
||||
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');
|
||||
},
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user