[Refactor] Convert dialogService to composable (#2058)

This commit is contained in:
Chenlei Hu
2024-12-26 00:26:01 -05:00
committed by GitHub
parent 4e554503c0
commit f69e88bf41
8 changed files with 176 additions and 171 deletions

View File

@@ -1,6 +1,6 @@
import { t } from '@/i18n'
import { app } from '@/scripts/app'
import { showConfirmationDialog } from '@/services/dialogService'
import { useDialogService } from '@/services/dialogService'
import { electronAPI as getElectronAPI, isElectron } from '@/utils/envUtil'
;(async () => {
if (!isElectron()) return
@@ -114,7 +114,7 @@ import { electronAPI as getElectronAPI, isElectron } from '@/utils/envUtil'
label: 'Reinstall',
icon: 'pi pi-refresh',
async function() {
const proceed = await showConfirmationDialog({
const proceed = await useDialogService().showConfirmationDialog({
message: t('desktopMenu.confirmReinstall'),
title: t('desktopMenu.reinstall'),
type: 'reinstall'

View File

@@ -14,7 +14,7 @@ import {
serialise
} from '@/extensions/core/vintageClipboard'
import type { ComfyNodeDef } from '@/types/apiTypes'
import { showPromptDialog } from '@/services/dialogService'
import { useDialogService } from '@/services/dialogService'
import { t } from '@/i18n'
type GroupNodeWorkflowData = {
@@ -80,7 +80,7 @@ class GroupNodeBuilder {
}
async getName() {
const name = await showPromptDialog({
const name = await useDialogService().showPromptDialog({
title: t('groupNode.create'),
message: t('groupNode.enterName'),
defaultValue: ''

View File

@@ -6,7 +6,7 @@ import { GroupNodeConfig, GroupNodeHandler } from './groupNode'
import { LGraphCanvas } from '@comfyorg/litegraph'
import { useToastStore } from '@/stores/toastStore'
import { deserialiseAndCreate } from '@/extensions/core/vintageClipboard'
import { showPromptDialog } from '@/services/dialogService'
import { useDialogService } from '@/services/dialogService'
import { t } from '@/i18n'
// Adds the ability to save and add multiple nodes as a template
@@ -351,7 +351,7 @@ app.registerExtension({
content: `Save Selected as Template`,
disabled: !Object.keys(app.canvas.selected_nodes || {}).length,
callback: async () => {
const name = await showPromptDialog({
const name = await useDialogService().showPromptDialog({
title: t('nodeTemplates.saveAsTemplate'),
message: t('nodeTemplates.enterName'),
defaultValue: ''

View File

@@ -1,9 +1,6 @@
import { app } from '@/scripts/app'
import { api } from '@/scripts/api'
import {
showSettingsDialog,
showTemplateWorkflowsDialog
} from '@/services/dialogService'
import { useDialogService } from '@/services/dialogService'
import { useWorkflowService } from '@/services/workflowService'
import type { ComfyCommand } from '@/stores/commandStore'
import { useTitleEditorStore } from '@/stores/graphStore'
@@ -23,6 +20,7 @@ import { useSearchBoxStore } from '@/stores/workspace/searchBoxStore'
export function useCoreCommands(): ComfyCommand[] {
const workflowService = useWorkflowService()
const dialogService = useDialogService()
const getTracker = () => useWorkflowStore()?.activeWorkflow?.changeTracker
const getSelectedNodes = (): LGraphNode[] => {
@@ -200,7 +198,9 @@ export function useCoreCommands(): ComfyCommand[] {
id: 'Comfy.BrowseTemplates',
icon: 'pi pi-folder-open',
label: 'Browse Templates',
function: showTemplateWorkflowsDialog
function: () => {
dialogService.showTemplateWorkflowsDialog()
}
},
{
id: 'Comfy.Canvas.ZoomIn',
@@ -302,7 +302,7 @@ export function useCoreCommands(): ComfyCommand[] {
label: 'Show Settings Dialog',
versionAdded: '1.3.7',
function: () => {
showSettingsDialog()
dialogService.showSettingsDialog()
}
},
{
@@ -509,7 +509,7 @@ export function useCoreCommands(): ComfyCommand[] {
menubarLabel: 'About ComfyUI',
versionAdded: '1.6.4',
function: () => {
showSettingsDialog('about')
dialogService.showSettingsDialog('about')
}
}
]

View File

@@ -41,11 +41,7 @@ import {
} from '@/stores/nodeDefStore'
import { INodeInputSlot, Vector2 } from '@comfyorg/litegraph'
import _ from 'lodash'
import {
showExecutionErrorDialog,
showLoadWorkflowWarning,
showMissingModelsWarning
} from '@/services/dialogService'
import { useDialogService } from '@/services/dialogService'
import { useSettingStore } from '@/stores/settingStore'
import { useToastStore } from '@/stores/toastStore'
import { useModelStore } from '@/stores/modelStore'
@@ -1528,7 +1524,7 @@ export class ComfyApp {
api.addEventListener('execution_error', ({ detail }) => {
this.lastExecutionError = detail
showExecutionErrorDialog(detail)
useDialogService().showExecutionErrorDialog(detail)
this.canvas.draw(true, true)
})
@@ -1992,13 +1988,13 @@ export class ComfyApp {
#showMissingNodesError(missingNodeTypes: MissingNodeType[]) {
if (useSettingStore().get('Comfy.Workflow.ShowMissingNodesWarning')) {
showLoadWorkflowWarning({ missingNodeTypes })
useDialogService().showLoadWorkflowWarning({ missingNodeTypes })
}
}
#showMissingModelsError(missingModels, paths) {
if (useSettingStore().get('Comfy.Workflow.ShowMissingModelsWarning')) {
showMissingModelsWarning({
useDialogService().showMissingModelsWarning({
missingModels,
paths
})

View File

@@ -5,7 +5,7 @@ import { toggleSwitch } from './ui/toggleSwitch'
import { ComfySettingsDialog } from './ui/settings'
import { ComfyApp, app } from './app'
import { TaskItem, type StatusWsMessageStatus } from '@/types/apiTypes'
import { showSettingsDialog } from '@/services/dialogService'
import { useDialogService } from '@/services/dialogService'
import { useSettingStore } from '@/stores/settingStore'
import { useCommandStore } from '@/stores/commandStore'
import { useWorkspaceStore } from '@/stores/workspaceStore'
@@ -438,7 +438,9 @@ export class ComfyUI {
$el('div.comfy-menu-actions', [
$el('button.comfy-settings-btn', {
textContent: '⚙️',
onclick: showSettingsDialog
onclick: () => {
useDialogService().showSettingsDialog()
}
}),
$el('button.comfy-close-menu-btn', {
textContent: '\u00d7',

View File

@@ -1,117 +1,15 @@
// This module is mocked in tests-ui/
// Import vue components here to avoid tests-ui/ reporting errors
// about importing primevue components.
import { useDialogStore, type ShowDialogOptions } from '@/stores/dialogStore'
import type { ExecutionErrorWsMessage } from '@/types/apiTypes'
import type { MissingNodeType } from '@/types/comfy'
import LoadWorkflowWarning from '@/components/dialog/content/LoadWorkflowWarning.vue'
import MissingModelsWarning from '@/components/dialog/content/MissingModelsWarning.vue'
import SettingDialogContent from '@/components/dialog/content/SettingDialogContent.vue'
import SettingDialogHeader from '@/components/dialog/header/SettingDialogHeader.vue'
import type { ExecutionErrorWsMessage } from '@/types/apiTypes'
import ExecutionErrorDialogContent from '@/components/dialog/content/ExecutionErrorDialogContent.vue'
import TemplateWorkflowsContent from '@/components/templates/TemplateWorkflowsContent.vue'
import PromptDialogContent from '@/components/dialog/content/PromptDialogContent.vue'
import ConfirmationDialogContent from '@/components/dialog/content/ConfirmationDialogContent.vue'
import { t } from '@/i18n'
import type { MissingNodeType } from '@/types/comfy'
export function showLoadWorkflowWarning(props: {
missingNodeTypes: MissingNodeType[]
[key: string]: any
}) {
const dialogStore = useDialogStore()
dialogStore.showDialog({
key: 'global-load-workflow-warning',
component: LoadWorkflowWarning,
props
})
}
export function showMissingModelsWarning(props: {
missingModels: any[]
paths: Record<string, string[]>
[key: string]: any
}) {
const dialogStore = useDialogStore()
dialogStore.showDialog({
key: 'global-missing-models-warning',
component: MissingModelsWarning,
props
})
}
export function showSettingsDialog(
panel?: 'about' | 'keybinding' | 'extension' | 'server-config'
) {
const props = panel ? { props: { defaultPanel: panel } } : undefined
useDialogStore().showDialog({
key: 'global-settings',
headerComponent: SettingDialogHeader,
component: SettingDialogContent,
...props
})
}
export function showAboutDialog() {
useDialogStore().showDialog({
key: 'global-settings',
headerComponent: SettingDialogHeader,
component: SettingDialogContent,
props: {
defaultPanel: 'about'
}
})
}
export function showExecutionErrorDialog(error: ExecutionErrorWsMessage) {
useDialogStore().showDialog({
key: 'global-execution-error',
component: ExecutionErrorDialogContent,
props: {
error
}
})
}
export function showTemplateWorkflowsDialog() {
useDialogStore().showDialog({
key: 'global-template-workflows',
title: t('templateWorkflows.title'),
component: TemplateWorkflowsContent
})
}
export async function showPromptDialog({
title,
message,
defaultValue = ''
}: {
title: string
message: string
defaultValue?: string
}): Promise<string | null> {
const dialogStore = useDialogStore()
return new Promise((resolve) => {
dialogStore.showDialog({
key: 'global-prompt',
title,
component: PromptDialogContent,
props: {
message,
defaultValue,
onConfirm: (value: string) => {
resolve(value)
}
},
dialogComponentProps: {
onClose: () => {
resolve(null)
}
}
})
})
}
export type ConfirmationDialogType =
| 'overwrite'
@@ -119,43 +17,151 @@ export type ConfirmationDialogType =
| 'dirtyClose'
| 'reinstall'
/**
*
* @returns `true` if the user confirms the dialog,
* `false` if denied (e.g. no in yes/no/cancel), or
* `null` if the dialog is cancelled or closed
*/
export async function showConfirmationDialog({
title,
type,
message,
itemList = []
}: {
/** Dialog heading */
title: string
/** Pre-configured dialog type */
type: ConfirmationDialogType
/** The main message body */
message: string
/** Displayed as an unorderd list immediately below the message body */
itemList?: string[]
}): Promise<boolean | null> {
return new Promise((resolve) => {
const options: ShowDialogOptions = {
key: 'global-prompt',
title,
component: ConfirmationDialogContent,
props: {
message,
type,
itemList,
onConfirm: resolve
},
dialogComponentProps: {
onClose: () => resolve(null)
}
}
export const useDialogService = () => {
const dialogStore = useDialogStore()
function showLoadWorkflowWarning(props: {
missingNodeTypes: MissingNodeType[]
[key: string]: any
}) {
dialogStore.showDialog({
key: 'global-load-workflow-warning',
component: LoadWorkflowWarning,
props
})
}
useDialogStore().showDialog(options)
})
function showMissingModelsWarning(props: {
missingModels: any[]
paths: Record<string, string[]>
[key: string]: any
}) {
dialogStore.showDialog({
key: 'global-missing-models-warning',
component: MissingModelsWarning,
props
})
}
function showSettingsDialog(
panel?: 'about' | 'keybinding' | 'extension' | 'server-config'
) {
const props = panel ? { props: { defaultPanel: panel } } : undefined
dialogStore.showDialog({
key: 'global-settings',
headerComponent: SettingDialogHeader,
component: SettingDialogContent,
...props
})
}
function showAboutDialog() {
dialogStore.showDialog({
key: 'global-settings',
headerComponent: SettingDialogHeader,
component: SettingDialogContent,
props: {
defaultPanel: 'about'
}
})
}
function showExecutionErrorDialog(error: ExecutionErrorWsMessage) {
dialogStore.showDialog({
key: 'global-execution-error',
component: ExecutionErrorDialogContent,
props: {
error
}
})
}
function showTemplateWorkflowsDialog() {
dialogStore.showDialog({
key: 'global-template-workflows',
title: t('templateWorkflows.title'),
component: TemplateWorkflowsContent
})
}
async function showPromptDialog({
title,
message,
defaultValue = ''
}: {
title: string
message: string
defaultValue?: string
}): Promise<string | null> {
return new Promise((resolve) => {
dialogStore.showDialog({
key: 'global-prompt',
title,
component: PromptDialogContent,
props: {
message,
defaultValue,
onConfirm: (value: string) => {
resolve(value)
}
},
dialogComponentProps: {
onClose: () => {
resolve(null)
}
}
})
})
}
/**
* @returns `true` if the user confirms the dialog,
* `false` if denied (e.g. no in yes/no/cancel), or
* `null` if the dialog is cancelled or closed
*/
async function showConfirmationDialog({
title,
type,
message,
itemList = []
}: {
/** Dialog heading */
title: string
/** Pre-configured dialog type */
type: ConfirmationDialogType
/** The main message body */
message: string
/** Displayed as an unorderd list immediately below the message body */
itemList?: string[]
}): Promise<boolean | null> {
return new Promise((resolve) => {
const options: ShowDialogOptions = {
key: 'global-prompt',
title,
component: ConfirmationDialogContent,
props: {
message,
type,
itemList,
onConfirm: resolve
},
dialogComponentProps: {
onClose: () => resolve(null)
}
}
dialogStore.showDialog(options)
})
}
return {
showLoadWorkflowWarning,
showMissingModelsWarning,
showSettingsDialog,
showAboutDialog,
showExecutionErrorDialog,
showTemplateWorkflowsDialog,
showPromptDialog,
showConfirmationDialog
}
}

View File

@@ -1,7 +1,7 @@
import { downloadBlob } from '@/scripts/utils'
import { useSettingStore } from '@/stores/settingStore'
import { useWorkflowStore, ComfyWorkflow } from '@/stores/workflowStore'
import { showConfirmationDialog, showPromptDialog } from './dialogService'
import { useDialogService } from './dialogService'
import { app } from '@/scripts/app'
import { useWorkspaceStore } from '@/stores/workspaceStore'
import { LGraphCanvas } from '@comfyorg/litegraph'
@@ -16,10 +16,11 @@ export const useWorkflowService = () => {
const settingStore = useSettingStore()
const workflowStore = useWorkflowStore()
const toastStore = useToastStore()
const dialogService = useDialogService()
async function getFilename(defaultName: string): Promise<string | null> {
if (settingStore.get('Comfy.PromptFilename')) {
let filename = await showPromptDialog({
let filename = await dialogService.showPromptDialog({
title: t('workflowService.exportWorkflow'),
message: t('workflowService.enterFilename') + ':',
defaultValue: defaultName
@@ -57,7 +58,7 @@ export const useWorkflowService = () => {
* @param workflow The workflow to save
*/
const saveWorkflowAs = async (workflow: ComfyWorkflow) => {
const newFilename = await showPromptDialog({
const newFilename = await dialogService.showPromptDialog({
title: t('workflowService.saveWorkflow'),
message: t('workflowService.enterFilename') + ':',
defaultValue: workflow.filename
@@ -69,7 +70,7 @@ export const useWorkflowService = () => {
const existingWorkflow = workflowStore.getWorkflowByPath(newPath)
if (existingWorkflow && !existingWorkflow.isTemporary) {
const res = await showConfirmationDialog({
const res = await dialogService.showConfirmationDialog({
title: t('sideToolbar.workflowTab.confirmOverwriteTitle'),
type: 'overwrite',
message: t('sideToolbar.workflowTab.confirmOverwrite'),
@@ -178,7 +179,7 @@ export const useWorkflowService = () => {
}
if (workflow.isModified && options.warnIfUnsaved) {
const confirmed = await showConfirmationDialog({
const confirmed = await dialogService.showConfirmationDialog({
title: t('sideToolbar.workflowTab.dirtyCloseTitle'),
type: 'dirtyClose',
message: t('sideToolbar.workflowTab.dirtyClose'),
@@ -222,7 +223,7 @@ export const useWorkflowService = () => {
let confirmed: boolean | null = bypassConfirm || silent
if (!confirmed) {
confirmed = await showConfirmationDialog({
confirmed = await dialogService.showConfirmationDialog({
title: t('sideToolbar.workflowTab.confirmDeleteTitle'),
type: 'delete',
message: t('sideToolbar.workflowTab.confirmDelete'),