diff --git a/.storybook/main.ts b/.storybook/main.ts index 0c576b0bde..68e22b2831 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -100,8 +100,7 @@ const config: StorybookConfig = { rolldownOptions: { treeshake: false, output: { - keepNames: true, - strictExecutionOrder: true + keepNames: true }, onwarn: (warning, warn) => { // Suppress specific warnings diff --git a/browser_tests/tests/viewport.spec.ts-snapshots/viewport-fits-when-saved-offscreen-chromium-linux.png b/browser_tests/tests/viewport.spec.ts-snapshots/viewport-fits-when-saved-offscreen-chromium-linux.png index 050c9d7086..4fa4bc57d9 100644 Binary files a/browser_tests/tests/viewport.spec.ts-snapshots/viewport-fits-when-saved-offscreen-chromium-linux.png and b/browser_tests/tests/viewport.spec.ts-snapshots/viewport-fits-when-saved-offscreen-chromium-linux.png differ diff --git a/src/composables/useHelpCenter.ts b/src/composables/useHelpCenter.ts index cb9dd2700c..20a08b9faa 100644 --- a/src/composables/useHelpCenter.ts +++ b/src/composables/useHelpCenter.ts @@ -4,11 +4,11 @@ import { computed, onMounted } from 'vue' import { useSettingStore } from '@/platform/settings/settingStore' import { useTelemetry } from '@/platform/telemetry' import { useReleaseStore } from '@/platform/updates/common/releaseStore' -import { useDialogService } from '@/services/dialogService' import { useHelpCenterStore } from '@/stores/helpCenterStore' import type { HelpCenterTriggerLocation } from '@/stores/helpCenterStore' import { useConflictAcknowledgment } from '@/workbench/extensions/manager/composables/useConflictAcknowledgment' import { useConflictDetection } from '@/workbench/extensions/manager/composables/useConflictDetection' +import { useNodeConflictDialog } from '@/workbench/extensions/manager/composables/useNodeConflictDialog' export function useHelpCenter( triggerFrom: HelpCenterTriggerLocation = 'sidebar' @@ -21,7 +21,7 @@ export function useHelpCenter( const { shouldShowRedDot: showReleaseRedDot } = storeToRefs(releaseStore) const conflictDetection = useConflictDetection() - const { showNodeConflictDialog } = useDialogService() + const { show: showNodeConflictDialog } = useNodeConflictDialog() // Use conflict acknowledgment state from composable - call only once const { shouldShowRedDot: shouldShowConflictRedDot, markConflictsAsSeen } = diff --git a/src/composables/useMissingModelsDialog.ts b/src/composables/useMissingModelsDialog.ts new file mode 100644 index 0000000000..d68a73d357 --- /dev/null +++ b/src/composables/useMissingModelsDialog.ts @@ -0,0 +1,26 @@ +import type { ComponentAttrs } from 'vue-component-type-helpers' + +import MissingModelsWarning from '@/components/dialog/content/MissingModelsWarning.vue' +import { useDialogService } from '@/services/dialogService' +import { useDialogStore } from '@/stores/dialogStore' + +const DIALOG_KEY = 'global-missing-models-warning' + +export function useMissingModelsDialog() { + const { showSmallLayoutDialog } = useDialogService() + const dialogStore = useDialogStore() + + function hide() { + dialogStore.closeDialog({ key: DIALOG_KEY }) + } + + function show(props: ComponentAttrs) { + showSmallLayoutDialog({ + key: DIALOG_KEY, + component: MissingModelsWarning, + props + }) + } + + return { show, hide } +} diff --git a/src/composables/useMissingNodesDialog.ts b/src/composables/useMissingNodesDialog.ts new file mode 100644 index 0000000000..83b7f156fe --- /dev/null +++ b/src/composables/useMissingNodesDialog.ts @@ -0,0 +1,30 @@ +import type { ComponentAttrs } from 'vue-component-type-helpers' + +import MissingNodesContent from '@/components/dialog/content/MissingNodesContent.vue' +import MissingNodesFooter from '@/components/dialog/content/MissingNodesFooter.vue' +import MissingNodesHeader from '@/components/dialog/content/MissingNodesHeader.vue' +import { useDialogService } from '@/services/dialogService' +import { useDialogStore } from '@/stores/dialogStore' + +const DIALOG_KEY = 'global-missing-nodes' + +export function useMissingNodesDialog() { + const { showSmallLayoutDialog } = useDialogService() + const dialogStore = useDialogStore() + + function hide() { + dialogStore.closeDialog({ key: DIALOG_KEY }) + } + + function show(props: ComponentAttrs) { + showSmallLayoutDialog({ + key: DIALOG_KEY, + headerComponent: MissingNodesHeader, + footerComponent: MissingNodesFooter, + component: MissingNodesContent, + props + }) + } + + return { show, hide } +} diff --git a/src/platform/workflow/core/services/workflowService.test.ts b/src/platform/workflow/core/services/workflowService.test.ts index c597c80a53..f27ff7683e 100644 --- a/src/platform/workflow/core/services/workflowService.test.ts +++ b/src/platform/workflow/core/services/workflowService.test.ts @@ -9,16 +9,21 @@ import { useWorkflowStore } from '@/platform/workflow/management/stores/workflow import { useWorkflowService } from '@/platform/workflow/core/services/workflowService' import { app } from '@/scripts/app' -const { mockShowLoadWorkflowWarning, mockShowMissingModelsWarning } = - vi.hoisted(() => ({ - mockShowLoadWorkflowWarning: vi.fn(), - mockShowMissingModelsWarning: vi.fn() - })) +const { mockShowMissingNodes, mockShowMissingModels } = vi.hoisted(() => ({ + mockShowMissingNodes: vi.fn(), + mockShowMissingModels: vi.fn() +})) + +vi.mock('@/composables/useMissingNodesDialog', () => ({ + useMissingNodesDialog: () => ({ show: mockShowMissingNodes, hide: vi.fn() }) +})) + +vi.mock('@/composables/useMissingModelsDialog', () => ({ + useMissingModelsDialog: () => ({ show: mockShowMissingModels, hide: vi.fn() }) +})) vi.mock('@/services/dialogService', () => ({ useDialogService: () => ({ - showLoadWorkflowWarning: mockShowLoadWorkflowWarning, - showMissingModelsWarning: mockShowMissingModelsWarning, prompt: vi.fn(), confirm: vi.fn() }) @@ -114,8 +119,8 @@ describe('useWorkflowService', () => { const workflow = createWorkflow(null) useWorkflowService().showPendingWarnings(workflow) - expect(mockShowLoadWorkflowWarning).not.toHaveBeenCalled() - expect(mockShowMissingModelsWarning).not.toHaveBeenCalled() + expect(mockShowMissingNodes).not.toHaveBeenCalled() + expect(mockShowMissingModels).not.toHaveBeenCalled() }) it('should show missing nodes dialog and clear warnings', () => { @@ -124,7 +129,7 @@ describe('useWorkflowService', () => { useWorkflowService().showPendingWarnings(workflow) - expect(mockShowLoadWorkflowWarning).toHaveBeenCalledWith({ + expect(mockShowMissingNodes).toHaveBeenCalledWith({ missingNodeTypes }) expect(workflow.pendingWarnings).toBeNull() @@ -135,7 +140,7 @@ describe('useWorkflowService', () => { useWorkflowService().showPendingWarnings(workflow) - expect(mockShowMissingModelsWarning).toHaveBeenCalledWith(MISSING_MODELS) + expect(mockShowMissingModels).toHaveBeenCalledWith(MISSING_MODELS) expect(workflow.pendingWarnings).toBeNull() }) @@ -149,8 +154,8 @@ describe('useWorkflowService', () => { useWorkflowService().showPendingWarnings(workflow) - expect(mockShowLoadWorkflowWarning).not.toHaveBeenCalled() - expect(mockShowMissingModelsWarning).not.toHaveBeenCalled() + expect(mockShowMissingNodes).not.toHaveBeenCalled() + expect(mockShowMissingModels).not.toHaveBeenCalled() expect(workflow.pendingWarnings).toBeNull() }) @@ -163,7 +168,7 @@ describe('useWorkflowService', () => { service.showPendingWarnings(workflow) service.showPendingWarnings(workflow) - expect(mockShowLoadWorkflowWarning).toHaveBeenCalledTimes(1) + expect(mockShowMissingNodes).toHaveBeenCalledTimes(1) }) }) @@ -188,7 +193,7 @@ describe('useWorkflowService', () => { { loadable: true } ) - expect(mockShowLoadWorkflowWarning).not.toHaveBeenCalled() + expect(mockShowMissingNodes).not.toHaveBeenCalled() await useWorkflowService().openWorkflow(workflow) @@ -199,7 +204,7 @@ describe('useWorkflowService', () => { workflow, expect.objectContaining({ deferWarnings: true }) ) - expect(mockShowLoadWorkflowWarning).toHaveBeenCalledWith({ + expect(mockShowMissingNodes).toHaveBeenCalledWith({ missingNodeTypes: ['CustomNode1'] }) expect(workflow.pendingWarnings).toBeNull() @@ -218,16 +223,16 @@ describe('useWorkflowService', () => { const service = useWorkflowService() await service.openWorkflow(workflow1) - expect(mockShowLoadWorkflowWarning).toHaveBeenCalledTimes(1) - expect(mockShowLoadWorkflowWarning).toHaveBeenCalledWith({ + expect(mockShowMissingNodes).toHaveBeenCalledTimes(1) + expect(mockShowMissingNodes).toHaveBeenCalledWith({ missingNodeTypes: ['MissingNodeA'] }) expect(workflow1.pendingWarnings).toBeNull() expect(workflow2.pendingWarnings).not.toBeNull() await service.openWorkflow(workflow2) - expect(mockShowLoadWorkflowWarning).toHaveBeenCalledTimes(2) - expect(mockShowLoadWorkflowWarning).toHaveBeenLastCalledWith({ + expect(mockShowMissingNodes).toHaveBeenCalledTimes(2) + expect(mockShowMissingNodes).toHaveBeenLastCalledWith({ missingNodeTypes: ['MissingNodeB'] }) expect(workflow2.pendingWarnings).toBeNull() @@ -242,10 +247,10 @@ describe('useWorkflowService', () => { const service = useWorkflowService() await service.openWorkflow(workflow, { force: true }) - expect(mockShowLoadWorkflowWarning).toHaveBeenCalledTimes(1) + expect(mockShowMissingNodes).toHaveBeenCalledTimes(1) await service.openWorkflow(workflow, { force: true }) - expect(mockShowLoadWorkflowWarning).toHaveBeenCalledTimes(1) + expect(mockShowMissingNodes).toHaveBeenCalledTimes(1) }) }) }) diff --git a/src/platform/workflow/core/services/workflowService.ts b/src/platform/workflow/core/services/workflowService.ts index 14d15f05af..96f1c02741 100644 --- a/src/platform/workflow/core/services/workflowService.ts +++ b/src/platform/workflow/core/services/workflowService.ts @@ -17,6 +17,8 @@ import { useCanvasStore } from '@/renderer/core/canvas/canvasStore' import { useWorkflowThumbnail } from '@/renderer/core/thumbnail/useWorkflowThumbnail' import { app } from '@/scripts/app' import { blankGraph, defaultGraph } from '@/scripts/defaultGraph' +import { useMissingModelsDialog } from '@/composables/useMissingModelsDialog' +import { useMissingNodesDialog } from '@/composables/useMissingNodesDialog' import { useDialogService } from '@/services/dialogService' import { useDomWidgetStore } from '@/stores/domWidgetStore' import { useWorkspaceStore } from '@/stores/workspaceStore' @@ -27,6 +29,8 @@ export const useWorkflowService = () => { const workflowStore = useWorkflowStore() const toastStore = useToastStore() const dialogService = useDialogService() + const missingModelsDialog = useMissingModelsDialog() + const missingNodesDialog = useMissingNodesDialog() const workflowThumbnail = useWorkflowThumbnail() const domWidgetStore = useDomWidgetStore() const workflowDraftStore = useWorkflowDraftStore() @@ -455,13 +459,13 @@ export const useWorkflowService = () => { missingNodeTypes?.length && settingStore.get('Comfy.Workflow.ShowMissingNodesWarning') ) { - void dialogService.showLoadWorkflowWarning({ missingNodeTypes }) + missingNodesDialog.show({ missingNodeTypes }) } if ( missingModels && settingStore.get('Comfy.Workflow.ShowMissingModelsWarning') ) { - void dialogService.showMissingModelsWarning(missingModels) + missingModelsDialog.show(missingModels) } } diff --git a/src/scripts/app.ts b/src/scripts/app.ts index a6fe39fdca..febf53ca39 100644 --- a/src/scripts/app.ts +++ b/src/scripts/app.ts @@ -52,6 +52,7 @@ import { } from '@/scripts/domWidget' import { useDialogService } from '@/services/dialogService' import { useBillingContext } from '@/composables/billing/useBillingContext' +import { useMissingNodesDialog } from '@/composables/useMissingNodesDialog' import { useExtensionService } from '@/services/extensionService' import { useLitegraphService } from '@/services/litegraphService' import { useSubgraphService } from '@/services/subgraphService' @@ -1068,7 +1069,7 @@ export class ComfyApp { private showMissingNodesError(missingNodeTypes: MissingNodeType[]) { if (useSettingStore().get('Comfy.Workflow.ShowMissingNodesWarning')) { - useDialogService().showLoadWorkflowWarning({ missingNodeTypes }) + useMissingNodesDialog().show({ missingNodeTypes }) } } diff --git a/src/services/dialogService.ts b/src/services/dialogService.ts index 0982892cf3..eb6dc2e7a2 100644 --- a/src/services/dialogService.ts +++ b/src/services/dialogService.ts @@ -16,22 +16,9 @@ import type { ShowDialogOptions } from '@/stores/dialogStore' -import type { ConflictDetectionResult } from '@/workbench/extensions/manager/types/conflictDetectionTypes' import type { ComponentAttrs } from 'vue-component-type-helpers' -// Type-only imports for ComponentAttrs inference (no runtime cost) -import type MissingNodesContent from '@/components/dialog/content/MissingNodesContent.vue' -import type MissingModelsWarning from '@/components/dialog/content/MissingModelsWarning.vue' - // Lazy loaders for dialogs - components are loaded on first use -const lazyMissingNodesContent = () => - import('@/components/dialog/content/MissingNodesContent.vue') -const lazyMissingNodesHeader = () => - import('@/components/dialog/content/MissingNodesHeader.vue') -const lazyMissingNodesFooter = () => - import('@/components/dialog/content/MissingNodesFooter.vue') -const lazyMissingModelsWarning = () => - import('@/components/dialog/content/MissingModelsWarning.vue') const lazyApiNodesSignInContent = () => import('@/components/dialog/content/ApiNodesSignInContent.vue') const lazySignInContent = () => @@ -40,18 +27,6 @@ const lazyUpdatePasswordContent = () => import('@/components/dialog/content/UpdatePasswordContent.vue') const lazyComfyOrgHeader = () => import('@/components/dialog/header/ComfyOrgHeader.vue') -const lazyImportFailedNodeContent = () => - import('@/workbench/extensions/manager/components/manager/ImportFailedNodeContent.vue') -const lazyImportFailedNodeHeader = () => - import('@/workbench/extensions/manager/components/manager/ImportFailedNodeHeader.vue') -const lazyImportFailedNodeFooter = () => - import('@/workbench/extensions/manager/components/manager/ImportFailedNodeFooter.vue') -const lazyNodeConflictDialogContent = () => - import('@/workbench/extensions/manager/components/manager/NodeConflictDialogContent.vue') -const lazyNodeConflictHeader = () => - import('@/workbench/extensions/manager/components/manager/NodeConflictHeader.vue') -const lazyNodeConflictFooter = () => - import('@/workbench/extensions/manager/components/manager/NodeConflictFooter.vue') export type ConfirmationDialogType = | 'default' @@ -77,56 +52,6 @@ export interface ExecutionErrorDialogInput { export const useDialogService = () => { const dialogStore = useDialogStore() - async function showLoadWorkflowWarning( - props: ComponentAttrs - ) { - const [ - { default: MissingNodesContent }, - { default: MissingNodesHeader }, - { default: MissingNodesFooter } - ] = await Promise.all([ - lazyMissingNodesContent(), - lazyMissingNodesHeader(), - lazyMissingNodesFooter() - ]) - - dialogStore.showDialog({ - key: 'global-missing-nodes', - headerComponent: MissingNodesHeader, - footerComponent: MissingNodesFooter, - component: MissingNodesContent, - dialogComponentProps: { - closable: true, - pt: { - root: { class: 'bg-base-background border-border-default' }, - header: { class: '!p-0 !m-0' }, - content: { class: '!p-0 overflow-y-hidden' }, - footer: { class: '!p-0' }, - pcCloseButton: { - root: { - class: '!w-7 !h-7 !border-none !outline-none !p-2 !m-1.5' - } - } - } - }, - props, - footerProps: { - missingNodeTypes: props.missingNodeTypes - } - }) - } - - async function showMissingModelsWarning( - props: ComponentAttrs - ) { - const { default: MissingModelsWarning } = await lazyMissingModelsWarning() - dialogStore.showDialog({ - key: 'global-missing-models-warning', - component: MissingModelsWarning, - props - }) - } - function showExecutionErrorDialog(executionError: ExecutionErrorDialogInput) { const props: ComponentAttrs = { error: { @@ -440,29 +365,15 @@ export const useDialogService = () => { }) } - async function showImportFailedNodeDialog( - options: { - conflictedPackages?: ConflictDetectionResult[] - dialogComponentProps?: DialogComponentProps - } = {} + function showSmallLayoutDialog( + options: Omit & { + dialogComponentProps?: Omit + } ) { - const [ - { default: ImportFailedNodeHeader }, - { default: ImportFailedNodeFooter }, - { default: ImportFailedNodeContent } - ] = await Promise.all([ - lazyImportFailedNodeHeader(), - lazyImportFailedNodeFooter(), - lazyImportFailedNodeContent() - ]) - - const { dialogComponentProps, conflictedPackages } = options + const { dialogComponentProps: callerProps, ...rest } = options return dialogStore.showDialog({ - key: 'global-import-failed', - headerComponent: ImportFailedNodeHeader, - footerComponent: ImportFailedNodeFooter, - component: ImportFailedNodeContent, + ...rest, dialogComponentProps: { closable: true, pt: { @@ -476,71 +387,7 @@ export const useDialogService = () => { } } }, - ...dialogComponentProps - }, - props: { - conflictedPackages: conflictedPackages ?? [] - }, - footerProps: { - conflictedPackages: conflictedPackages ?? [] - } - }) - } - - async function showNodeConflictDialog( - options: { - showAfterWhatsNew?: boolean - conflictedPackages?: ConflictDetectionResult[] - dialogComponentProps?: DialogComponentProps - buttonText?: string - onButtonClick?: () => void - } = {} - ) { - const [ - { default: NodeConflictHeader }, - { default: NodeConflictFooter }, - { default: NodeConflictDialogContent } - ] = await Promise.all([ - lazyNodeConflictHeader(), - lazyNodeConflictFooter(), - lazyNodeConflictDialogContent() - ]) - - const { - dialogComponentProps, - buttonText, - onButtonClick, - showAfterWhatsNew, - conflictedPackages - } = options - - return dialogStore.showDialog({ - key: 'global-node-conflict', - headerComponent: NodeConflictHeader, - footerComponent: NodeConflictFooter, - component: NodeConflictDialogContent, - dialogComponentProps: { - closable: true, - pt: { - header: { class: '!p-0 !m-0' }, - content: { class: '!p-0 overflow-y-hidden' }, - footer: { class: '!p-0' }, - pcCloseButton: { - root: { - class: - '!w-7 !h-7 !border-none !outline-none !p-2 !m-1.5 bg-dialog-surface text-white' - } - } - }, - ...dialogComponentProps - }, - props: { - showAfterWhatsNew, - conflictedPackages - }, - footerProps: { - buttonText, - onButtonClick + ...callerProps } }) } @@ -715,8 +562,6 @@ export const useDialogService = () => { } return { - showLoadWorkflowWarning, - showMissingModelsWarning, showExecutionErrorDialog, showApiNodesSignInDialog, showSignInDialog, @@ -728,8 +573,7 @@ export const useDialogService = () => { showErrorDialog, confirm, showLayoutDialog, - showImportFailedNodeDialog, - showNodeConflictDialog, + showSmallLayoutDialog, showDeleteWorkspaceDialog, showCreateWorkspaceDialog, showLeaveWorkspaceDialog, diff --git a/src/workbench/extensions/manager/components/manager/button/PackEnableToggle.vue b/src/workbench/extensions/manager/components/manager/button/PackEnableToggle.vue index a43ec76fc0..91cb65f93d 100644 --- a/src/workbench/extensions/manager/components/manager/button/PackEnableToggle.vue +++ b/src/workbench/extensions/manager/components/manager/button/PackEnableToggle.vue @@ -36,11 +36,11 @@ import ToggleSwitch from 'primevue/toggleswitch' import { computed, ref } from 'vue' import { useI18n } from 'vue-i18n' -import { useDialogService } from '@/services/dialogService' import type { components } from '@/types/comfyRegistryTypes' import { useConflictAcknowledgment } from '@/workbench/extensions/manager/composables/useConflictAcknowledgment' import { useImportFailedDetection } from '@/workbench/extensions/manager/composables/useImportFailedDetection' import { useComfyManagerStore } from '@/workbench/extensions/manager/stores/comfyManagerStore' +import { useNodeConflictDialog } from '@/workbench/extensions/manager/composables/useNodeConflictDialog' import { useConflictDetectionStore } from '@/workbench/extensions/manager/stores/conflictDetectionStore' import type { components as ManagerComponents } from '@/workbench/extensions/manager/types/generatedManagerTypes' @@ -54,7 +54,7 @@ const { t } = useI18n() const { isPackEnabled, enablePack, disablePack, installedPacks } = useComfyManagerStore() const { getConflictsForPackageByID } = useConflictDetectionStore() -const { showNodeConflictDialog } = useDialogService() +const { show: showNodeConflictDialog } = useNodeConflictDialog() const { acknowledgmentState, markConflictsAsSeen } = useConflictAcknowledgment() const { showImportFailedDialog } = useImportFailedDetection(nodePack.id || '') diff --git a/src/workbench/extensions/manager/components/manager/button/PackInstallButton.vue b/src/workbench/extensions/manager/components/manager/button/PackInstallButton.vue index 0aeab79fe4..ba3f60fe5d 100644 --- a/src/workbench/extensions/manager/components/manager/button/PackInstallButton.vue +++ b/src/workbench/extensions/manager/components/manager/button/PackInstallButton.vue @@ -26,9 +26,9 @@ import { useI18n } from 'vue-i18n' import DotSpinner from '@/components/common/DotSpinner.vue' import Button from '@/components/ui/button/Button.vue' import type { ButtonVariants } from '@/components/ui/button/button.variants' -import { useDialogService } from '@/services/dialogService' import type { components } from '@/types/comfyRegistryTypes' import { useConflictDetection } from '@/workbench/extensions/manager/composables/useConflictDetection' +import { useNodeConflictDialog } from '@/workbench/extensions/manager/composables/useNodeConflictDialog' import { useComfyManagerStore } from '@/workbench/extensions/manager/stores/comfyManagerStore' import type { ConflictDetail, @@ -55,7 +55,7 @@ const { }>() const managerStore = useComfyManagerStore() -const { showNodeConflictDialog } = useDialogService() +const { show: showNodeConflictDialog } = useNodeConflictDialog() const { t } = useI18n() // Check if any of the packs are currently being installed diff --git a/src/workbench/extensions/manager/composables/useImportFailedDetection.test.ts b/src/workbench/extensions/manager/composables/useImportFailedDetection.test.ts index 83944f4f89..a89309d281 100644 --- a/src/workbench/extensions/manager/composables/useImportFailedDetection.test.ts +++ b/src/workbench/extensions/manager/composables/useImportFailedDetection.test.ts @@ -3,15 +3,30 @@ import { setActivePinia } from 'pinia' import { beforeEach, describe, expect, it, vi } from 'vitest' import { computed, ref } from 'vue' -import * as dialogService from '@/services/dialogService' import { useImportFailedDetection } from '@/workbench/extensions/manager/composables/useImportFailedDetection' -import * as comfyManagerStore from '@/workbench/extensions/manager/stores/comfyManagerStore' -import * as conflictDetectionStore from '@/workbench/extensions/manager/stores/conflictDetectionStore' -// Mock the stores and services -vi.mock('@/workbench/extensions/manager/stores/comfyManagerStore') -vi.mock('@/workbench/extensions/manager/stores/conflictDetectionStore') -vi.mock('@/services/dialogService') +const mockIsPackInstalled = vi.fn() +const mockGetConflictsForPackageByID = vi.fn() +const mockShow = vi.fn() + +vi.mock('@/workbench/extensions/manager/stores/comfyManagerStore', () => ({ + useComfyManagerStore: () => ({ + isPackInstalled: mockIsPackInstalled + }) +})) +vi.mock('@/workbench/extensions/manager/stores/conflictDetectionStore', () => ({ + useConflictDetectionStore: () => ({ + getConflictsForPackageByID: mockGetConflictsForPackageByID + }) +})) +vi.mock( + '@/workbench/extensions/manager/composables/useImportFailedNodeDialog', + () => ({ + useImportFailedNodeDialog: () => ({ + show: mockShow + }) + }) +) vi.mock('vue-i18n', async () => { const actual = await vi.importActual('vue-i18n') return { @@ -23,43 +38,13 @@ vi.mock('vue-i18n', async () => { }) describe('useImportFailedDetection', () => { - let mockComfyManagerStore: ReturnType< - typeof comfyManagerStore.useComfyManagerStore - > - let mockConflictDetectionStore: ReturnType< - typeof conflictDetectionStore.useConflictDetectionStore - > - let mockDialogService: ReturnType - beforeEach(() => { setActivePinia(createTestingPinia({ stubActions: false })) - - mockComfyManagerStore = { - isPackInstalled: vi.fn() - } as unknown as ReturnType - - mockConflictDetectionStore = { - getConflictsForPackageByID: vi.fn() - } as unknown as ReturnType< - typeof conflictDetectionStore.useConflictDetectionStore - > - - mockDialogService = { - showErrorDialog: vi.fn(), - showImportFailedNodeDialog: vi.fn() - } as unknown as ReturnType - - vi.mocked(comfyManagerStore.useComfyManagerStore).mockReturnValue( - mockComfyManagerStore - ) - vi.mocked(conflictDetectionStore.useConflictDetectionStore).mockReturnValue( - mockConflictDetectionStore - ) - vi.mocked(dialogService.useDialogService).mockReturnValue(mockDialogService) + vi.clearAllMocks() }) it('should return false for importFailed when package is not installed', () => { - vi.mocked(mockComfyManagerStore.isPackInstalled).mockReturnValue(false) + mockIsPackInstalled.mockReturnValue(false) const { importFailed } = useImportFailedDetection('test-package') @@ -67,10 +52,8 @@ describe('useImportFailedDetection', () => { }) it('should return false for importFailed when no conflicts exist', () => { - vi.mocked(mockComfyManagerStore.isPackInstalled).mockReturnValue(true) - vi.mocked( - mockConflictDetectionStore.getConflictsForPackageByID - ).mockReturnValue(undefined) + mockIsPackInstalled.mockReturnValue(true) + mockGetConflictsForPackageByID.mockReturnValue(undefined) const { importFailed } = useImportFailedDetection('test-package') @@ -78,10 +61,8 @@ describe('useImportFailedDetection', () => { }) it('should return false for importFailed when conflicts exist but no import_failed type', () => { - vi.mocked(mockComfyManagerStore.isPackInstalled).mockReturnValue(true) - vi.mocked( - mockConflictDetectionStore.getConflictsForPackageByID - ).mockReturnValue({ + mockIsPackInstalled.mockReturnValue(true) + mockGetConflictsForPackageByID.mockReturnValue({ package_id: 'test-package', package_name: 'Test Package', has_conflict: true, @@ -106,10 +87,8 @@ describe('useImportFailedDetection', () => { }) it('should return true for importFailed when import_failed conflicts exist', () => { - vi.mocked(mockComfyManagerStore.isPackInstalled).mockReturnValue(true) - vi.mocked( - mockConflictDetectionStore.getConflictsForPackageByID - ).mockReturnValue({ + mockIsPackInstalled.mockReturnValue(true) + mockGetConflictsForPackageByID.mockReturnValue({ package_id: 'test-package', package_name: 'Test Package', has_conflict: true, @@ -135,10 +114,8 @@ describe('useImportFailedDetection', () => { it('should work with computed ref packageId', () => { const packageId = ref('test-package') - vi.mocked(mockComfyManagerStore.isPackInstalled).mockReturnValue(true) - vi.mocked( - mockConflictDetectionStore.getConflictsForPackageByID - ).mockReturnValue({ + mockIsPackInstalled.mockReturnValue(true) + mockGetConflictsForPackageByID.mockReturnValue({ package_id: 'test-package', package_name: 'Test Package', has_conflict: true, @@ -160,9 +137,7 @@ describe('useImportFailedDetection', () => { // Change packageId packageId.value = 'another-package' - vi.mocked( - mockConflictDetectionStore.getConflictsForPackageByID - ).mockReturnValue(undefined) + mockGetConflictsForPackageByID.mockReturnValue(undefined) expect(importFailed.value).toBe(false) }) @@ -181,10 +156,8 @@ describe('useImportFailedDetection', () => { } ] - vi.mocked(mockComfyManagerStore.isPackInstalled).mockReturnValue(true) - vi.mocked( - mockConflictDetectionStore.getConflictsForPackageByID - ).mockReturnValue({ + mockIsPackInstalled.mockReturnValue(true) + mockGetConflictsForPackageByID.mockReturnValue({ package_id: 'test-package', package_name: 'Test Package', has_conflict: true, @@ -213,10 +186,8 @@ describe('useImportFailedDetection', () => { } ] - vi.mocked(mockComfyManagerStore.isPackInstalled).mockReturnValue(true) - vi.mocked( - mockConflictDetectionStore.getConflictsForPackageByID - ).mockReturnValue({ + mockIsPackInstalled.mockReturnValue(true) + mockGetConflictsForPackageByID.mockReturnValue({ package_id: 'test-package', package_name: 'Test Package', has_conflict: true, @@ -228,7 +199,7 @@ describe('useImportFailedDetection', () => { showImportFailedDialog() - expect(mockDialogService.showImportFailedNodeDialog).toHaveBeenCalledWith({ + expect(mockShow).toHaveBeenCalledWith({ conflictedPackages: expect.arrayContaining([ expect.objectContaining({ package_id: 'test-package', diff --git a/src/workbench/extensions/manager/composables/useImportFailedDetection.ts b/src/workbench/extensions/manager/composables/useImportFailedDetection.ts index d125255c8e..55da784e52 100644 --- a/src/workbench/extensions/manager/composables/useImportFailedDetection.ts +++ b/src/workbench/extensions/manager/composables/useImportFailedDetection.ts @@ -1,7 +1,7 @@ import { computed, unref } from 'vue' import type { ComputedRef } from 'vue' -import { useDialogService } from '@/services/dialogService' +import { useImportFailedNodeDialog } from '@/workbench/extensions/manager/composables/useImportFailedNodeDialog' import { useComfyManagerStore } from '@/workbench/extensions/manager/stores/comfyManagerStore' import { useConflictDetectionStore } from '@/workbench/extensions/manager/stores/conflictDetectionStore' import type { @@ -26,14 +26,14 @@ function extractImportFailedConflicts(conflicts?: ConflictDetail[] | null) { * Creating import failed dialog */ function createImportFailedDialog() { - const { showImportFailedNodeDialog } = useDialogService() + const { show } = useImportFailedNodeDialog() return ( conflictedPackages: ConflictDetectionResult[] | null, onClose?: () => void ) => { if (conflictedPackages && conflictedPackages.length > 0) { - void showImportFailedNodeDialog({ + void show({ conflictedPackages, dialogComponentProps: { onClose diff --git a/src/workbench/extensions/manager/composables/useImportFailedNodeDialog.ts b/src/workbench/extensions/manager/composables/useImportFailedNodeDialog.ts new file mode 100644 index 0000000000..35e56ed199 --- /dev/null +++ b/src/workbench/extensions/manager/composables/useImportFailedNodeDialog.ts @@ -0,0 +1,43 @@ +import { useDialogService } from '@/services/dialogService' +import type { DialogComponentProps } from '@/stores/dialogStore' +import { useDialogStore } from '@/stores/dialogStore' +import ImportFailedNodeContent from '@/workbench/extensions/manager/components/manager/ImportFailedNodeContent.vue' +import ImportFailedNodeFooter from '@/workbench/extensions/manager/components/manager/ImportFailedNodeFooter.vue' +import ImportFailedNodeHeader from '@/workbench/extensions/manager/components/manager/ImportFailedNodeHeader.vue' +import type { ConflictDetectionResult } from '@/workbench/extensions/manager/types/conflictDetectionTypes' + +const DIALOG_KEY = 'global-import-failed' + +export function useImportFailedNodeDialog() { + const { showSmallLayoutDialog } = useDialogService() + const dialogStore = useDialogStore() + + function hide() { + dialogStore.closeDialog({ key: DIALOG_KEY }) + } + + function show( + options: { + conflictedPackages?: ConflictDetectionResult[] + dialogComponentProps?: Omit + } = {} + ) { + const { dialogComponentProps, conflictedPackages = [] } = options + + showSmallLayoutDialog({ + key: DIALOG_KEY, + headerComponent: ImportFailedNodeHeader, + footerComponent: ImportFailedNodeFooter, + component: ImportFailedNodeContent, + dialogComponentProps, + props: { + conflictedPackages + }, + footerProps: { + conflictedPackages + } + }) + } + + return { show, hide } +} diff --git a/src/workbench/extensions/manager/composables/useNodeConflictDialog.ts b/src/workbench/extensions/manager/composables/useNodeConflictDialog.ts new file mode 100644 index 0000000000..1eda5916db --- /dev/null +++ b/src/workbench/extensions/manager/composables/useNodeConflictDialog.ts @@ -0,0 +1,54 @@ +import { useDialogService } from '@/services/dialogService' +import type { DialogComponentProps } from '@/stores/dialogStore' +import { useDialogStore } from '@/stores/dialogStore' +import NodeConflictDialogContent from '@/workbench/extensions/manager/components/manager/NodeConflictDialogContent.vue' +import NodeConflictFooter from '@/workbench/extensions/manager/components/manager/NodeConflictFooter.vue' +import NodeConflictHeader from '@/workbench/extensions/manager/components/manager/NodeConflictHeader.vue' +import type { ConflictDetectionResult } from '@/workbench/extensions/manager/types/conflictDetectionTypes' + +const DIALOG_KEY = 'global-node-conflict' + +export function useNodeConflictDialog() { + const { showSmallLayoutDialog } = useDialogService() + const dialogStore = useDialogStore() + + function hide() { + dialogStore.closeDialog({ key: DIALOG_KEY }) + } + + function show( + options: { + showAfterWhatsNew?: boolean + conflictedPackages?: ConflictDetectionResult[] + dialogComponentProps?: Omit + buttonText?: string + onButtonClick?: () => void + } = {} + ) { + const { + dialogComponentProps, + buttonText, + onButtonClick, + showAfterWhatsNew, + conflictedPackages + } = options + + showSmallLayoutDialog({ + key: DIALOG_KEY, + headerComponent: NodeConflictHeader, + footerComponent: NodeConflictFooter, + component: NodeConflictDialogContent, + dialogComponentProps, + props: { + showAfterWhatsNew, + conflictedPackages + }, + footerProps: { + buttonText, + onButtonClick + } + }) + } + + return { show, hide } +}