feat(editor): Workflow history [WIP] - Remove pinned data from workflow history version preview (no-changelog) (#7406)

This commit is contained in:
Csaba Tuncsik
2023-10-19 14:38:00 +02:00
committed by GitHub
parent 82129694c6
commit c7c8048430
6 changed files with 447 additions and 195 deletions

View File

@@ -8,202 +8,194 @@
</div>
<iframe
:class="{
[$style.workflow]: !this.nodeViewDetailsOpened,
[$style.workflow]: !nodeViewDetailsOpened,
[$style.executionPreview]: mode === 'execution',
[$style.openNDV]: this.nodeViewDetailsOpened,
[$style.show]: this.showPreview,
[$style.openNDV]: nodeViewDetailsOpened,
[$style.show]: showPreview,
}"
ref="preview_iframe"
ref="iframeRef"
:src="`${rootStore.baseUrl}workflows/demo`"
@mouseenter="onMouseEnter"
@mouseleave="onMouseLeave"
></iframe>
/>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { useToast } from '@/composables';
<script setup lang="ts">
import { onMounted, onBeforeUnmount, ref, computed, watch } from 'vue';
import { useI18n, useToast } from '@/composables';
import type { IWorkflowDb } from '@/Interface';
import { mapStores } from 'pinia';
import { useRootStore } from '@/stores/n8nRoot.store';
import { useWorkflowsStore } from '@/stores';
import { useWorkflowsStore } from '@/stores/workflows.store';
export default defineComponent({
name: 'WorkflowPreview',
props: {
loading: {
type: Boolean,
default: false,
},
mode: {
type: String,
default: 'workflow',
validator: (value: string): boolean => ['workflow', 'execution'].includes(value),
},
workflow: {
type: Object as () => IWorkflowDb,
required: false,
},
executionId: {
type: String,
required: false,
},
executionMode: {
type: String,
required: false,
},
loaderType: {
type: String,
default: 'image',
validator: (value: string): boolean => ['image', 'spinner'].includes(value),
},
const props = withDefaults(
defineProps<{
loading?: boolean;
mode?: 'workflow' | 'execution';
workflow?: IWorkflowDb;
executionId?: string;
executionMode?: string;
loaderType?: 'image' | 'spinner';
}>(),
{
loading: false,
mode: 'workflow',
loaderType: 'image',
},
setup() {
return {
...useToast(),
};
},
data() {
return {
nodeViewDetailsOpened: false,
ready: false,
insideIframe: false,
scrollX: 0,
scrollY: 0,
};
},
computed: {
...mapStores(useRootStore, useWorkflowsStore),
showPreview(): boolean {
return (
!this.loading &&
((this.mode === 'workflow' && !!this.workflow) ||
(this.mode === 'execution' && !!this.executionId)) &&
this.ready
);
},
},
methods: {
onMouseEnter() {
this.insideIframe = true;
this.scrollX = window.scrollX;
this.scrollY = window.scrollY;
},
onMouseLeave() {
this.insideIframe = false;
},
loadWorkflow() {
try {
if (!this.workflow) {
throw new Error(this.$locale.baseText('workflowPreview.showError.missingWorkflow'));
}
if (!this.workflow.nodes || !Array.isArray(this.workflow.nodes)) {
throw new Error(this.$locale.baseText('workflowPreview.showError.arrayEmpty'));
}
);
const iframeRef = this.$refs.preview_iframe as HTMLIFrameElement | undefined;
if (iframeRef?.contentWindow) {
iframeRef.contentWindow.postMessage(
JSON.stringify({
command: 'openWorkflow',
workflow: this.workflow,
}),
'*',
);
}
} catch (error) {
this.showError(
error,
this.$locale.baseText('workflowPreview.showError.previewError.title'),
this.$locale.baseText('workflowPreview.showError.previewError.message'),
);
}
},
loadExecution() {
try {
if (!this.executionId) {
throw new Error(this.$locale.baseText('workflowPreview.showError.missingExecution'));
}
const iframeRef = this.$refs.preview_iframe as HTMLIFrameElement | undefined;
if (iframeRef?.contentWindow) {
iframeRef.contentWindow.postMessage(
JSON.stringify({
command: 'openExecution',
executionId: this.executionId,
executionMode: this.executionMode || '',
}),
'*',
);
const emit = defineEmits<{
(event: 'close'): void;
}>();
if (this.workflowsStore.activeWorkflowExecution) {
iframeRef.contentWindow.postMessage(
JSON.stringify({
command: 'setActiveExecution',
execution: this.workflowsStore.activeWorkflowExecution,
}),
'*',
);
}
}
} catch (error) {
this.showError(
error,
this.$locale.baseText('workflowPreview.showError.previewError.title'),
this.$locale.baseText('workflowPreview.executionMode.showError.previewError.message'),
);
}
},
receiveMessage({ data }: MessageEvent) {
try {
const json = JSON.parse(data);
if (json.command === 'n8nReady') {
this.ready = true;
} else if (json.command === 'openNDV') {
this.nodeViewDetailsOpened = true;
} else if (json.command === 'closeNDV') {
this.nodeViewDetailsOpened = false;
} else if (json.command === 'error') {
this.$emit('close');
}
} catch (e) {}
},
onDocumentScroll() {
if (this.insideIframe) {
window.scrollTo(this.scrollX, this.scrollY);
}
},
},
watch: {
showPreview(show) {
if (show) {
if (this.mode === 'workflow') {
this.loadWorkflow();
} else if (this.mode === 'execution') {
this.loadExecution();
}
}
},
executionId(value) {
if (this.mode === 'execution' && this.executionId) {
this.loadExecution();
}
},
workflow() {
if (this.mode === 'workflow' && this.workflow) {
this.loadWorkflow();
}
},
},
mounted() {
window.addEventListener('message', this.receiveMessage);
document.addEventListener('scroll', this.onDocumentScroll);
},
beforeUnmount() {
window.removeEventListener('message', this.receiveMessage);
document.removeEventListener('scroll', this.onDocumentScroll);
},
const i18n = useI18n();
const toast = useToast();
const rootStore = useRootStore();
const workflowsStore = useWorkflowsStore();
const iframeRef = ref<HTMLIFrameElement | null>(null);
const nodeViewDetailsOpened = ref(false);
const ready = ref(false);
const insideIframe = ref(false);
const scrollX = ref(0);
const scrollY = ref(0);
const showPreview = computed(() => {
return (
!props.loading &&
((props.mode === 'workflow' && props.workflow) ||
(props.mode === 'execution' && props.executionId)) &&
ready.value
);
});
const loadWorkflow = () => {
try {
if (!props.workflow) {
throw new Error(i18n.baseText('workflowPreview.showError.missingWorkflow'));
}
if (!props.workflow.nodes || !Array.isArray(props.workflow.nodes)) {
throw new Error(i18n.baseText('workflowPreview.showError.arrayEmpty'));
}
iframeRef.value?.contentWindow?.postMessage?.(
JSON.stringify({
command: 'openWorkflow',
workflow: props.workflow,
}),
'*',
);
} catch (error) {
toast.showError(
error,
i18n.baseText('workflowPreview.showError.previewError.title'),
i18n.baseText('workflowPreview.showError.previewError.message'),
);
}
};
const loadExecution = () => {
try {
if (!props.executionId) {
throw new Error(i18n.baseText('workflowPreview.showError.missingExecution'));
}
iframeRef.value?.contentWindow?.postMessage?.(
JSON.stringify({
command: 'openExecution',
executionId: props.executionId,
executionMode: props.executionMode || '',
}),
'*',
);
if (workflowsStore.activeWorkflowExecution) {
iframeRef.value?.contentWindow?.postMessage?.(
JSON.stringify({
command: 'setActiveExecution',
execution: workflowsStore.activeWorkflowExecution,
}),
'*',
);
}
} catch (error) {
toast.showError(
error,
i18n.baseText('workflowPreview.showError.previewError.title'),
i18n.baseText('workflowPreview.executionMode.showError.previewError.message'),
);
}
};
const onMouseEnter = () => {
insideIframe.value = true;
scrollX.value = window.scrollX;
scrollY.value = window.scrollY;
};
const onMouseLeave = () => {
insideIframe.value = false;
};
const receiveMessage = ({ data }: MessageEvent) => {
try {
const json = JSON.parse(data);
if (json.command === 'n8nReady') {
ready.value = true;
} else if (json.command === 'openNDV') {
nodeViewDetailsOpened.value = true;
} else if (json.command === 'closeNDV') {
nodeViewDetailsOpened.value = false;
} else if (json.command === 'error') {
emit('close');
}
} catch (e) {
console.error(e);
}
};
const onDocumentScroll = () => {
if (insideIframe.value) {
window.scrollTo(scrollX.value, scrollY.value);
}
};
onMounted(() => {
window.addEventListener('message', receiveMessage);
document.addEventListener('scroll', onDocumentScroll);
});
onBeforeUnmount(() => {
window.removeEventListener('message', receiveMessage);
document.removeEventListener('scroll', onDocumentScroll);
});
watch(
() => showPreview.value,
() => {
if (showPreview.value) {
if (props.mode === 'workflow') {
loadWorkflow();
} else if (props.mode === 'execution') {
loadExecution();
}
}
},
);
watch(
() => props.executionId,
() => {
if (props.mode === 'execution' && props.executionId) {
loadExecution();
}
},
);
watch(
() => props.workflow,
() => {
if (props.mode === 'workflow' && props.workflow) {
loadWorkflow();
}
},
);
</script>
<style lang="scss" module>