From 2c34bf4ea6137fb0fb321969684ffa621da20fa3 Mon Sep 17 00:00:00 2001 From: Michael Kret <88898367+michael-radency@users.noreply.github.com> Date: Wed, 27 Nov 2024 15:19:06 +0200 Subject: [PATCH] fix(n8n Form Node): Duplicate popup in manual mode (#11925) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ --- .../editor-ui/src/stores/workflows.store.ts | 13 ++------- .../src/utils/executionUtils.test.ts | 10 +++---- .../editor-ui/src/utils/executionUtils.ts | 29 ++++++++++--------- 3 files changed, 24 insertions(+), 28 deletions(-) diff --git a/packages/editor-ui/src/stores/workflows.store.ts b/packages/editor-ui/src/stores/workflows.store.ts index f49beaed84781..58452eaf34249 100644 --- a/packages/editor-ui/src/stores/workflows.store.ts +++ b/packages/editor-ui/src/stores/workflows.store.ts @@ -84,7 +84,7 @@ import { TelemetryHelpers } from 'n8n-workflow'; import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers'; import { useRouter } from 'vue-router'; import { useSettingsStore } from './settings.store'; -import { openPopUpWindow } from '@/utils/executionUtils'; +import { closeFormPopupWindow, openFormPopupWindow } from '@/utils/executionUtils'; import { useNodeHelpers } from '@/composables/useNodeHelpers'; const defaults: Omit & { settings: NonNullable } = { @@ -143,7 +143,6 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, () => { const chatMessages = ref([]); const isChatPanelOpen = ref(false); const isLogsPanelOpen = ref(false); - const formPopupWindow = ref(null); const workflowName = computed(() => workflow.value.name); @@ -1319,12 +1318,7 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, () => { (node.type === WAIT_NODE_TYPE && node.parameters.resume === 'form') ) { const testUrl = getFormResumeUrl(node, executionId); - if (!formPopupWindow.value || formPopupWindow.value.closed) { - formPopupWindow.value = openPopUpWindow(testUrl); - } else { - formPopupWindow.value.location = testUrl; - formPopupWindow.value.focus(); - } + openFormPopupWindow(testUrl); } } else { if (tasksData.length && tasksData[tasksData.length - 1].executionStatus === 'waiting') { @@ -1577,8 +1571,7 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, () => { uiStore.removeActiveAction('workflowRunning'); workflowHelpers.setDocumentTitle(workflowName.value, 'IDLE'); - formPopupWindow.value?.close(); - formPopupWindow.value = null; + closeFormPopupWindow(); const runData = workflowExecutionData.value?.data?.resultData.runData ?? {}; for (const nodeName in runData) { diff --git a/packages/editor-ui/src/utils/executionUtils.test.ts b/packages/editor-ui/src/utils/executionUtils.test.ts index abcab9509fcc4..a4f87cf3f1099 100644 --- a/packages/editor-ui/src/utils/executionUtils.test.ts +++ b/packages/editor-ui/src/utils/executionUtils.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { displayForm, - openPopUpWindow, + openFormPopupWindow, executionFilterToQueryFilter, waitingNodeTooltip, } from './executionUtils'; @@ -15,7 +15,7 @@ vi.mock('./executionUtils', async () => { const actual = await vi.importActual('./executionUtils'); return { ...actual, - openPopUpWindow: vi.fn(), + openFormPopupWindow: vi.fn(), }; }); @@ -86,7 +86,7 @@ describe('displayForm', () => { getTestUrl: getTestUrlMock, }); - expect(openPopUpWindow).not.toHaveBeenCalled(); + expect(openFormPopupWindow).not.toHaveBeenCalled(); }); it('should skip nodes if destinationNode does not match and node is not a directParentNode', () => { @@ -119,7 +119,7 @@ describe('displayForm', () => { getTestUrl: getTestUrlMock, }); - expect(openPopUpWindow).not.toHaveBeenCalled(); + expect(openFormPopupWindow).not.toHaveBeenCalled(); }); it('should not open pop-up if source is "RunData.ManualChatMessage"', () => { @@ -146,7 +146,7 @@ describe('displayForm', () => { getTestUrl: getTestUrlMock, }); - expect(openPopUpWindow).not.toHaveBeenCalled(); + expect(openFormPopupWindow).not.toHaveBeenCalled(); }); describe('executionFilterToQueryFilter()', () => { diff --git a/packages/editor-ui/src/utils/executionUtils.ts b/packages/editor-ui/src/utils/executionUtils.ts index 014f3492b638d..d8db7b8f8e950 100644 --- a/packages/editor-ui/src/utils/executionUtils.ts +++ b/packages/editor-ui/src/utils/executionUtils.ts @@ -82,25 +82,28 @@ export const executionFilterToQueryFilter = ( return queryFilter; }; -export const openPopUpWindow = ( - url: string, - options?: { width?: number; height?: number; alwaysInNewTab?: boolean }, -) => { - const windowWidth = window.innerWidth; - const smallScreen = windowWidth <= 800; - if (options?.alwaysInNewTab || smallScreen) { - return window.open(url, '_blank'); - } else { - const height = options?.width || 700; - const width = options?.height || window.innerHeight - 50; +let formPopupWindow: Window | null = null; + +export const openFormPopupWindow = (url: string) => { + if (!formPopupWindow || formPopupWindow.closed) { + const height = 700; + const width = window.innerHeight - 50; const left = (window.innerWidth - height) / 2; const top = 50; const features = `width=${height},height=${width},left=${left},top=${top},resizable=yes,scrollbars=yes`; const windowName = `form-waiting-since-${Date.now()}`; - return window.open(url, windowName, features); + formPopupWindow = window.open(url, windowName, features); + } else { + formPopupWindow.location = url; + formPopupWindow.focus(); } }; +export const closeFormPopupWindow = () => { + formPopupWindow?.close(); + formPopupWindow = null; +}; + export function displayForm({ nodes, runData, @@ -131,7 +134,7 @@ export function displayForm({ if (node.name === destinationNode || !node.disabled) { let testUrl = ''; if (node.type === FORM_TRIGGER_NODE_TYPE) testUrl = getTestUrl(node); - if (testUrl && source !== 'RunData.ManualChatMessage') openPopUpWindow(testUrl); + if (testUrl && source !== 'RunData.ManualChatMessage') openFormPopupWindow(testUrl); } } }