import { whenever } from '@vueuse/core' import { orderBy } from 'es-toolkit/compat' import type { Ref } from 'vue' import { computed } from 'vue' import { useRegistrySearchGateway } from '@/services/gateway/registrySearchGateway' import type { components } from '@/types/comfyRegistryTypes' import { useInstalledPacks } from '@/workbench/extensions/manager/composables/nodePack/useInstalledPacks' import { usePackUpdateStatus } from '@/workbench/extensions/manager/composables/nodePack/usePackUpdateStatus' import { useWorkflowPacks } from '@/workbench/extensions/manager/composables/nodePack/useWorkflowPacks' import { useComfyManagerStore } from '@/workbench/extensions/manager/stores/comfyManagerStore' import { useConflictDetectionStore } from '@/workbench/extensions/manager/stores/conflictDetectionStore' import { ManagerTab } from '@/workbench/extensions/manager/types/comfyManagerTypes' type NodePack = components['schemas']['Node'] export function useManagerDisplayPacks( selectedTabId: Ref, searchResults: Ref, searchQuery: Ref, sortField: Ref ) { const comfyManagerStore = useComfyManagerStore() const conflictDetectionStore = useConflictDetectionStore() const { getSortValue, getSortableFields } = useRegistrySearchGateway() const { startFetchInstalled, filterInstalledPack, installedPacks, isLoading: isLoadingInstalled, isReady: installedPacksReady } = useInstalledPacks() const { startFetchWorkflowPacks, filterWorkflowPack, workflowPacks, isLoading: isLoadingWorkflow, isReady: workflowPacksReady } = useWorkflowPacks() const tabType = computed(() => selectedTabId.value as ManagerTab | null) const isEmptySearch = computed(() => searchQuery.value === '') // Sorting function for packs not from searchResults const sortPacks = (packs: NodePack[]) => { if (!sortField.value || packs.length === 0) return packs const sortableFields = getSortableFields() const fieldConfig = sortableFields.find((f) => f.id === sortField.value) const direction = fieldConfig?.direction || 'desc' return orderBy( packs, [(pack) => getSortValue(pack, sortField.value)], [direction] ) } // Filter functions const filterNotInstalled = (packs: NodePack[]) => packs.filter((p) => !comfyManagerStore.isPackInstalled(p.id)) const filterConflicting = (packs: NodePack[]) => packs.filter( (p) => !!p.id && conflictDetectionStore.conflictedPackages.some( (c) => c.package_id === p.id ) ) const filterOutdated = (packs: NodePack[]) => packs.filter((p) => { const { isUpdateAvailable } = usePackUpdateStatus(p) return isUpdateAvailable.value }) const filterMissing = (packs: NodePack[]) => packs.filter((p) => !comfyManagerStore.isPackInstalled(p.id)) // Data fetching triggers using whenever const needsInstalledPacks = computed(() => [ ManagerTab.AllInstalled, ManagerTab.UpdateAvailable, ManagerTab.Conflicting ].includes(tabType.value as ManagerTab) ) const needsWorkflowPacks = computed(() => [ManagerTab.Workflow, ManagerTab.Missing].includes( tabType.value as ManagerTab ) ) whenever( () => needsInstalledPacks.value && !installedPacksReady.value && !isLoadingInstalled.value, () => startFetchInstalled() ) whenever( () => needsWorkflowPacks.value && !workflowPacksReady.value && !isLoadingWorkflow.value, () => startFetchWorkflowPacks() ) // For Missing tab, also need installed packs to determine what's missing whenever( () => tabType.value === ManagerTab.Missing && !installedPacksReady.value && !isLoadingInstalled.value, () => startFetchInstalled() ) // Single computed for display packs - replaces 7 watches const displayPacks = computed(() => { const tab = tabType.value const hasSearch = !isEmptySearch.value switch (tab) { case ManagerTab.All: return searchResults.value case ManagerTab.NotInstalled: return filterNotInstalled(searchResults.value) case ManagerTab.AllInstalled: return hasSearch ? filterInstalledPack(searchResults.value) : sortPacks(installedPacks.value) case ManagerTab.UpdateAvailable: return sortPacks( filterOutdated( hasSearch ? filterInstalledPack(searchResults.value) : installedPacks.value ) ) case ManagerTab.Conflicting: return sortPacks( filterConflicting( hasSearch ? filterInstalledPack(searchResults.value) : installedPacks.value ) ) case ManagerTab.Workflow: { return hasSearch ? filterWorkflowPack(searchResults.value) : sortPacks(workflowPacks.value) } case ManagerTab.Missing: { const base = hasSearch ? filterWorkflowPack(searchResults.value) : workflowPacks.value return sortPacks(filterMissing(base)) } default: return searchResults.value } }) // Loading state - single computed const isLoading = computed(() => { const tab = tabType.value if ( [ ManagerTab.AllInstalled, ManagerTab.UpdateAvailable, ManagerTab.Conflicting ].includes(tab as ManagerTab) ) { return isLoadingInstalled.value } if ([ManagerTab.Workflow, ManagerTab.Missing].includes(tab as ManagerTab)) { return isLoadingWorkflow.value } return false }) const missingNodePacks = computed(() => filterMissing(workflowPacks.value)) return { displayPacks, isLoading, isLoadingInstalled, isLoadingWorkflow, installedPacks, workflowPacks, filterInstalledPack, filterWorkflowPack, missingNodePacks } }