mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-20 23:04:06 +00:00
[refactor] Unify small modal dialog styles with showSmallLayoutDialog (#8834)
## Summary Extract a shared `showSmallLayoutDialog` utility and move dialog-specific logic into composables, unifying the duplicated `pt` configurations across small modal dialogs. ## Changes - **`showSmallLayoutDialog`**: Added to `dialogService.ts` with a single unified `pt` config for all small modal dialogs (missing nodes, missing models, import failed, node conflict) - **Composables**: Extracted 4 dialog functions from `dialogService` into dedicated composables following the `useSettingsDialog` / `useModelSelectorDialog` pattern: - `useMissingNodesDialog` - `useMissingModelsDialog` - `useImportFailedNodeDialog` - `useNodeConflictDialog` - Each composable uses direct imports, synchronous `show()`, `hide()`, and a `DIALOG_KEY` constant - Updated all call sites (`app.ts`, `useHelpCenter`, `PackEnableToggle`, `PackInstallButton`, `useImportFailedDetection`) ## Review Focus - Unified `pt` config removes minor style variations between dialogs — intentional design unification ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-8834-refactor-Unify-small-modal-dialog-styles-with-showSmallLayoutDialog-3056d73d365081b6963beffc0e5943bf) by [Unito](https://www.unito.io) --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
@@ -100,8 +100,7 @@ const config: StorybookConfig = {
|
||||
rolldownOptions: {
|
||||
treeshake: false,
|
||||
output: {
|
||||
keepNames: true,
|
||||
strictExecutionOrder: true
|
||||
keepNames: true
|
||||
},
|
||||
onwarn: (warning, warn) => {
|
||||
// Suppress specific warnings
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 74 KiB |
@@ -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 } =
|
||||
|
||||
26
src/composables/useMissingModelsDialog.ts
Normal file
26
src/composables/useMissingModelsDialog.ts
Normal file
@@ -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<typeof MissingModelsWarning>) {
|
||||
showSmallLayoutDialog({
|
||||
key: DIALOG_KEY,
|
||||
component: MissingModelsWarning,
|
||||
props
|
||||
})
|
||||
}
|
||||
|
||||
return { show, hide }
|
||||
}
|
||||
30
src/composables/useMissingNodesDialog.ts
Normal file
30
src/composables/useMissingNodesDialog.ts
Normal file
@@ -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<typeof MissingNodesContent>) {
|
||||
showSmallLayoutDialog({
|
||||
key: DIALOG_KEY,
|
||||
headerComponent: MissingNodesHeader,
|
||||
footerComponent: MissingNodesFooter,
|
||||
component: MissingNodesContent,
|
||||
props
|
||||
})
|
||||
}
|
||||
|
||||
return { show, hide }
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<typeof MissingNodesContent>
|
||||
) {
|
||||
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<typeof MissingModelsWarning>
|
||||
) {
|
||||
const { default: MissingModelsWarning } = await lazyMissingModelsWarning()
|
||||
dialogStore.showDialog({
|
||||
key: 'global-missing-models-warning',
|
||||
component: MissingModelsWarning,
|
||||
props
|
||||
})
|
||||
}
|
||||
|
||||
function showExecutionErrorDialog(executionError: ExecutionErrorDialogInput) {
|
||||
const props: ComponentAttrs<typeof ErrorDialogContent> = {
|
||||
error: {
|
||||
@@ -440,29 +365,15 @@ export const useDialogService = () => {
|
||||
})
|
||||
}
|
||||
|
||||
async function showImportFailedNodeDialog(
|
||||
options: {
|
||||
conflictedPackages?: ConflictDetectionResult[]
|
||||
dialogComponentProps?: DialogComponentProps
|
||||
} = {}
|
||||
function showSmallLayoutDialog(
|
||||
options: Omit<ShowDialogOptions, 'dialogComponentProps'> & {
|
||||
dialogComponentProps?: Omit<DialogComponentProps, 'pt'>
|
||||
}
|
||||
) {
|
||||
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,
|
||||
|
||||
@@ -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 || '')
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<typeof dialogService.useDialogService>
|
||||
|
||||
beforeEach(() => {
|
||||
setActivePinia(createTestingPinia({ stubActions: false }))
|
||||
|
||||
mockComfyManagerStore = {
|
||||
isPackInstalled: vi.fn()
|
||||
} as unknown as ReturnType<typeof comfyManagerStore.useComfyManagerStore>
|
||||
|
||||
mockConflictDetectionStore = {
|
||||
getConflictsForPackageByID: vi.fn()
|
||||
} as unknown as ReturnType<
|
||||
typeof conflictDetectionStore.useConflictDetectionStore
|
||||
>
|
||||
|
||||
mockDialogService = {
|
||||
showErrorDialog: vi.fn(),
|
||||
showImportFailedNodeDialog: vi.fn()
|
||||
} as unknown as ReturnType<typeof dialogService.useDialogService>
|
||||
|
||||
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',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<DialogComponentProps, 'pt'>
|
||||
} = {}
|
||||
) {
|
||||
const { dialogComponentProps, conflictedPackages = [] } = options
|
||||
|
||||
showSmallLayoutDialog({
|
||||
key: DIALOG_KEY,
|
||||
headerComponent: ImportFailedNodeHeader,
|
||||
footerComponent: ImportFailedNodeFooter,
|
||||
component: ImportFailedNodeContent,
|
||||
dialogComponentProps,
|
||||
props: {
|
||||
conflictedPackages
|
||||
},
|
||||
footerProps: {
|
||||
conflictedPackages
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return { show, hide }
|
||||
}
|
||||
@@ -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<DialogComponentProps, 'pt'>
|
||||
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 }
|
||||
}
|
||||
Reference in New Issue
Block a user