refactor: extract multi-package logic into reusable composables

- Create usePackageSelection composable for installation state management
- Create usePackageStatus composable for status priority logic
- Refactor InfoPanelMultiItem to use new composables
- Reduce component complexity by separating business logic
- Improve code reusability across components

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Jin Yi
2025-08-18 11:13:03 +09:00
parent accd039de5
commit 3eb8504429
3 changed files with 131 additions and 72 deletions

View File

@@ -55,7 +55,7 @@
<script setup lang="ts">
import { useAsyncState } from '@vueuse/core'
import { computed, onUnmounted, provide } from 'vue'
import { computed, onUnmounted, provide, toRef } from 'vue'
import PackStatusMessage from '@/components/dialog/content/manager/PackStatusMessage.vue'
import PackInstallButton from '@/components/dialog/content/manager/button/PackInstallButton.vue'
@@ -64,9 +64,9 @@ import InfoPanelHeader from '@/components/dialog/content/manager/infoPanel/InfoP
import MetadataRow from '@/components/dialog/content/manager/infoPanel/MetadataRow.vue'
import PackIconStacked from '@/components/dialog/content/manager/packIcon/PackIconStacked.vue'
import { useConflictDetection } from '@/composables/useConflictDetection'
import { useComfyManagerStore } from '@/stores/comfyManagerStore'
import { usePackageSelection } from '@/composables/usePackageSelection'
import { usePackageStatus } from '@/composables/usePackageStatus'
import { useComfyRegistryStore } from '@/stores/comfyRegistryStore'
import { useConflictDetectionStore } from '@/stores/conflictDetectionStore'
import { components } from '@/types/comfyRegistryTypes'
import type { ConflictDetail } from '@/types/conflictDetectionTypes'
import { ImportFailedKey } from '@/types/importFailedTypes'
@@ -75,51 +75,29 @@ const { nodePacks } = defineProps<{
nodePacks: components['schemas']['Node'][]
}>()
const managerStore = useComfyManagerStore()
const conflictDetectionStore = useConflictDetectionStore()
const nodePacksRef = toRef(() => nodePacks)
// Use new composables for cleaner code
const {
installedPacks,
notInstalledPacks,
isAllInstalled,
isNoneInstalled,
isMixed
} = usePackageSelection(nodePacksRef)
const { hasImportFailed, overallStatus } = usePackageStatus(nodePacksRef)
const { checkNodeCompatibility } = useConflictDetection()
const { getNodeDefs } = useComfyRegistryStore()
// Check if any package has import failed status
const hasImportFailed = computed(() => {
return nodePacks.some((pack) => {
if (!pack.id) return false
const conflicts = conflictDetectionStore.getConflictsForPackageByID(pack.id)
return (
conflicts?.conflicts?.some((c) => c.type === 'import_failed') || false
)
})
})
// Provide import failed context for PackStatusMessage
provide(ImportFailedKey, {
importFailed: hasImportFailed,
showImportFailedDialog: () => {} // No-op for multi-selection
})
// Check installation status
const installedPacks = computed(() =>
nodePacks.filter((pack) => managerStore.isPackInstalled(pack.id))
)
const notInstalledPacks = computed(() =>
nodePacks.filter((pack) => !managerStore.isPackInstalled(pack.id))
)
const isAllInstalled = computed(
() => installedPacks.value.length === nodePacks.length
)
const isNoneInstalled = computed(
() => notInstalledPacks.value.length === nodePacks.length
)
const isMixed = computed(
() => installedPacks.value.length > 0 && notInstalledPacks.value.length > 0
)
// Check for conflicts in not-installed packages - store per package
// Check for conflicts in not-installed packages - keep original logic but simplified
const packageConflicts = computed(() => {
const conflictsByPackage = new Map<string, ConflictDetail[]>()
@@ -151,39 +129,6 @@ const conflictInfo = computed<ConflictDetail[]>(() => {
const hasConflicts = computed(() => conflictInfo.value.length > 0)
// Determine the most important status from all selected packages
const overallStatus = computed(() => {
// Check for import failed first (highest priority for installed packages)
if (hasImportFailed.value) {
// Import failed doesn't have a specific status enum, so we return active
// but the PackStatusMessage will handle it via hasImportFailed prop
return 'NodeVersionStatusActive' as components['schemas']['NodeVersionStatus']
}
// Priority order: banned > deleted > flagged > pending > active
const statusPriority = [
'NodeStatusBanned',
'NodeVersionStatusBanned',
'NodeStatusDeleted',
'NodeVersionStatusDeleted',
'NodeVersionStatusFlagged',
'NodeVersionStatusPending',
'NodeStatusActive',
'NodeVersionStatusActive'
]
for (const priorityStatus of statusPriority) {
if (nodePacks.some((pack) => pack.status === priorityStatus)) {
return priorityStatus as
| components['schemas']['NodeStatus']
| components['schemas']['NodeVersionStatus']
}
}
// Default to active if no specific status found
return 'NodeVersionStatusActive' as components['schemas']['NodeVersionStatus']
})
const getPackNodes = async (pack: components['schemas']['Node']) => {
if (!pack.latest_version?.version) return []
const nodeDefs = await getNodeDefs.call({

View File

@@ -0,0 +1,51 @@
import { type Ref, computed } from 'vue'
import { useComfyManagerStore } from '@/stores/comfyManagerStore'
import type { components } from '@/types/comfyRegistryTypes'
type NodePack = components['schemas']['Node']
export type SelectionState = 'all-installed' | 'none-installed' | 'mixed'
/**
* Composable for managing multi-package selection states
* Handles installation status tracking and selection state determination
*/
export function usePackageSelection(nodePacks: Ref<NodePack[]>) {
const managerStore = useComfyManagerStore()
const installedPacks = computed(() =>
nodePacks.value.filter((pack) => managerStore.isPackInstalled(pack.id))
)
const notInstalledPacks = computed(() =>
nodePacks.value.filter((pack) => !managerStore.isPackInstalled(pack.id))
)
const isAllInstalled = computed(
() => installedPacks.value.length === nodePacks.value.length
)
const isNoneInstalled = computed(
() => notInstalledPacks.value.length === nodePacks.value.length
)
const isMixed = computed(
() => installedPacks.value.length > 0 && notInstalledPacks.value.length > 0
)
const selectionState = computed<SelectionState>(() => {
if (isAllInstalled.value) return 'all-installed'
if (isNoneInstalled.value) return 'none-installed'
return 'mixed'
})
return {
installedPacks,
notInstalledPacks,
isAllInstalled,
isNoneInstalled,
isMixed,
selectionState
}
}

View File

@@ -0,0 +1,63 @@
import { type Ref, computed } from 'vue'
import { useConflictDetectionStore } from '@/stores/conflictDetectionStore'
import type { components } from '@/types/comfyRegistryTypes'
type NodePack = components['schemas']['Node']
type NodeStatus = components['schemas']['NodeStatus']
type NodeVersionStatus = components['schemas']['NodeVersionStatus']
const STATUS_PRIORITY = [
'NodeStatusBanned',
'NodeVersionStatusBanned',
'NodeStatusDeleted',
'NodeVersionStatusDeleted',
'NodeVersionStatusFlagged',
'NodeVersionStatusPending',
'NodeStatusActive',
'NodeVersionStatusActive'
] as const
/**
* Composable for managing package status with priority
* Handles import failures and determines the most important status
*/
export function usePackageStatus(nodePacks: Ref<NodePack[]>) {
const conflictDetectionStore = useConflictDetectionStore()
const hasImportFailed = computed(() => {
return nodePacks.value.some((pack) => {
if (!pack.id) return false
const conflicts = conflictDetectionStore.getConflictsForPackageByID(
pack.id
)
return (
conflicts?.conflicts?.some((c) => c.type === 'import_failed') || false
)
})
})
const overallStatus = computed<NodeStatus | NodeVersionStatus>(() => {
// Check for import failed first (highest priority for installed packages)
if (hasImportFailed.value) {
// Import failed doesn't have a specific status enum, so we return active
// but the PackStatusMessage will handle it via hasImportFailed prop
return 'NodeVersionStatusActive' as NodeVersionStatus
}
// Find the highest priority status from all packages
for (const priorityStatus of STATUS_PRIORITY) {
if (nodePacks.value.some((pack) => pack.status === priorityStatus)) {
return priorityStatus as NodeStatus | NodeVersionStatus
}
}
// Default to active if no specific status found
return 'NodeVersionStatusActive' as NodeVersionStatus
})
return {
hasImportFailed,
overallStatus
}
}