Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
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 <[email protected]>
  • Loading branch information
viva-jinyi and claude committed Aug 27, 2025
commit 5186d6572fa3e4b8680668e7c33db25d10cf515e
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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'
Expand All @@ -75,51 +75,29 @@ const { nodePacks } = defineProps<{
nodePacks: components['schemas']['Node'][]
}>()

const managerStore = useComfyManagerStore()
const conflictDetectionStore = useConflictDetectionStore()
const { checkNodeCompatibility } = useConflictDetection()
const nodePacksRef = toRef(() => nodePacks)

const { getNodeDefs } = useComfyRegistryStore()
// Use new composables for cleaner code
const {
installedPacks,
notInstalledPacks,
isAllInstalled,
isNoneInstalled,
isMixed
} = usePackageSelection(nodePacksRef)

// 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
)
})
})
const { hasImportFailed, overallStatus } = usePackageStatus(nodePacksRef)

const { checkNodeCompatibility } = useConflictDetection()
const { getNodeDefs } = useComfyRegistryStore()

// 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[]>()

Expand Down Expand Up @@ -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({
Expand Down
51 changes: 51 additions & 0 deletions src/composables/usePackageSelection.ts
Original file line number Diff line number Diff line change
@@ -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
}
}
63 changes: 63 additions & 0 deletions src/composables/usePackageStatus.ts
Original file line number Diff line number Diff line change
@@ -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
}
}