Skip to content

Commit b44394f

Browse files
viva-jinyichristian-byrne
authored andcommitted
[Manager] Add update all button functionality
- Add PackUpdateButton component for bulk updates - Add useUpdateAvailableNodes composable to track available updates - Integrate update all button in RegistrySearchBar - Add localization strings for update functionality - Add comprehensive tests for update functionality - Add loading state to PackActionButton
1 parent e53dcb8 commit b44394f

File tree

13 files changed

+528
-0
lines changed

13 files changed

+528
-0
lines changed

src/components/dialog/content/manager/ManagerDialogContent.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
:suggestions="suggestions"
3535
:is-missing-tab="isMissingTab"
3636
:sort-options="sortOptions"
37+
:is-update-available-tab="isUpdateAvailableTab"
3738
/>
3839
<div class="flex-1 overflow-auto">
3940
<div

src/components/dialog/content/manager/button/PackActionButton.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import Button from 'primevue/button'
2828
2929
const {
3030
label,
31+
loading = false,
3132
loadingMessage,
3233
fullWidth = false,
3334
variant = 'default'
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<template>
2+
<PackActionButton
3+
v-bind="$attrs"
4+
variant="black"
5+
:label="$t('manager.updateAll')"
6+
:loading="isUpdating"
7+
:loading-message="$t('g.updating')"
8+
@action="updateAllPacks"
9+
/>
10+
</template>
11+
12+
<script setup lang="ts">
13+
import { ref } from 'vue'
14+
15+
import PackActionButton from '@/components/dialog/content/manager/button/PackActionButton.vue'
16+
import { useComfyManagerStore } from '@/stores/comfyManagerStore'
17+
import type { components } from '@/types/comfyRegistryTypes'
18+
19+
type NodePack = components['schemas']['Node']
20+
21+
const { nodePacks } = defineProps<{
22+
nodePacks: NodePack[]
23+
}>()
24+
25+
const isUpdating = ref<boolean>(false)
26+
27+
const managerStore = useComfyManagerStore()
28+
29+
const createPayload = (updateItem: NodePack) => {
30+
return {
31+
id: updateItem.id!,
32+
version: updateItem.latest_version!.version!
33+
}
34+
}
35+
36+
const updatePack = async (item: NodePack) => {
37+
if (!item.id || !item.latest_version?.version) {
38+
console.warn('Pack missing required id or version:', item)
39+
return
40+
}
41+
await managerStore.updatePack.call(createPayload(item))
42+
}
43+
44+
const updateAllPacks = async () => {
45+
if (!nodePacks?.length) {
46+
console.warn('No packs provided for update')
47+
return
48+
}
49+
50+
isUpdating.value = true
51+
52+
const updatablePacks = nodePacks.filter((pack) =>
53+
managerStore.isPackInstalled(pack.id)
54+
)
55+
56+
if (!updatablePacks.length) {
57+
console.info('No installed packs available for update')
58+
isUpdating.value = false
59+
return
60+
}
61+
62+
console.info(`Starting update of ${updatablePacks.length} packs`)
63+
64+
try {
65+
await Promise.all(updatablePacks.map(updatePack))
66+
managerStore.updatePack.clear()
67+
console.info('All packs updated successfully')
68+
} catch (error) {
69+
console.error('Pack update failed:', error)
70+
console.error(
71+
'Failed packs info:',
72+
updatablePacks.map((p) => p.id)
73+
)
74+
} finally {
75+
isUpdating.value = false
76+
}
77+
}
78+
</script>

src/components/dialog/content/manager/registrySearchBar/RegistrySearchBar.vue

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@
3333
:node-packs="missingNodePacks"
3434
:label="$t('manager.installAllMissingNodes')"
3535
/>
36+
<PackUpdateButton
37+
v-if="isUpdateAvailableTab && hasUpdateAvailable"
38+
:node-packs="updateAvailableNodePacks"
39+
/>
3640
</div>
3741
<div class="flex mt-3 text-sm">
3842
<div class="flex gap-6 ml-1">
@@ -65,8 +69,10 @@ import { computed } from 'vue'
6569
import { useI18n } from 'vue-i18n'
6670
6771
import PackInstallButton from '@/components/dialog/content/manager/button/PackInstallButton.vue'
72+
import PackUpdateButton from '@/components/dialog/content/manager/button/PackUpdateButton.vue'
6873
import SearchFilterDropdown from '@/components/dialog/content/manager/registrySearchBar/SearchFilterDropdown.vue'
6974
import { useMissingNodes } from '@/composables/nodePack/useMissingNodes'
75+
import { useUpdateAvailableNodes } from '@/composables/nodePack/useUpdateAvailableNodes'
7076
import {
7177
type SearchOption,
7278
SortableAlgoliaField
@@ -83,6 +89,7 @@ const { searchResults, sortOptions } = defineProps<{
8389
suggestions?: QuerySuggestion[]
8490
sortOptions?: SortableField[]
8591
isMissingTab?: boolean
92+
isUpdateAvailableTab?: boolean
8693
}>()
8794
8895
const searchQuery = defineModel<string>('searchQuery')
@@ -96,6 +103,10 @@ const { t } = useI18n()
96103
// Get missing node packs from workflow with loading and error states
97104
const { missingNodePacks, isLoading, error } = useMissingNodes()
98105
106+
// Use the composable to get update available nodes
107+
const { hasUpdateAvailable, updateAvailableNodePacks } =
108+
useUpdateAvailableNodes()
109+
99110
const hasResults = computed(
100111
() => searchQuery.value?.trim() && searchResults?.length
101112
)
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { computed, onMounted } from 'vue'
2+
3+
import { useInstalledPacks } from '@/composables/nodePack/useInstalledPacks'
4+
import { useComfyManagerStore } from '@/stores/comfyManagerStore'
5+
import type { components } from '@/types/comfyRegistryTypes'
6+
import { compareVersions, isSemVer } from '@/utils/formatUtil'
7+
8+
/**
9+
* Composable to find NodePacks that have updates available
10+
* Uses the same filtering approach as ManagerDialogContent.vue
11+
* Automatically fetches installed pack data when initialized
12+
*/
13+
export const useUpdateAvailableNodes = () => {
14+
const comfyManagerStore = useComfyManagerStore()
15+
const { installedPacks, isLoading, error, startFetchInstalled } =
16+
useInstalledPacks()
17+
18+
// Check if a pack has updates available (same logic as usePackUpdateStatus)
19+
const isOutdatedPack = (pack: components['schemas']['Node']) => {
20+
const isInstalled = comfyManagerStore.isPackInstalled(pack?.id)
21+
if (!isInstalled) return false
22+
23+
const installedVersion = comfyManagerStore.getInstalledPackVersion(
24+
pack.id ?? ''
25+
)
26+
const latestVersion = pack.latest_version?.version
27+
28+
const isNightlyPack = !!installedVersion && !isSemVer(installedVersion)
29+
30+
if (isNightlyPack || !latestVersion) {
31+
return false
32+
}
33+
34+
return compareVersions(latestVersion, installedVersion) > 0
35+
}
36+
37+
// Same filtering logic as ManagerDialogContent.vue
38+
const filterOutdatedPacks = (packs: components['schemas']['Node'][]) =>
39+
packs.filter(isOutdatedPack)
40+
41+
// Filter only outdated packs from installed packs
42+
const updateAvailableNodePacks = computed(() => {
43+
if (!installedPacks.value.length) return []
44+
return filterOutdatedPacks(installedPacks.value)
45+
})
46+
47+
// Check if there are any outdated packs
48+
const hasUpdateAvailable = computed(() => {
49+
return updateAvailableNodePacks.value.length > 0
50+
})
51+
52+
// Automatically fetch installed pack data when composable is used
53+
onMounted(async () => {
54+
if (!installedPacks.value.length && !isLoading.value) {
55+
await startFetchInstalled()
56+
}
57+
})
58+
59+
return {
60+
updateAvailableNodePacks,
61+
hasUpdateAvailable,
62+
isLoading,
63+
error
64+
}
65+
}

src/locales/en/main.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,8 @@
148148
"uninstalling": "Uninstalling",
149149
"update": "Update",
150150
"uninstallSelected": "Uninstall Selected",
151+
"updateSelected": "Update Selected",
152+
"updateAll": "Update All",
151153
"updatingAllPacks": "Updating all packages",
152154
"license": "License",
153155
"nightlyVersion": "Nightly",

src/locales/es/main.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,8 @@
635635
"uninstallSelected": "Desinstalar Seleccionado",
636636
"uninstalling": "Desinstalando",
637637
"update": "Actualizar",
638+
"updateSelected": "Actualizar seleccionados",
639+
"updateAll": "Actualizar todo",
638640
"updatingAllPacks": "Actualizando todos los paquetes",
639641
"version": "Versión"
640642
},

src/locales/fr/main.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,8 @@
635635
"uninstallSelected": "Désinstaller sélectionné",
636636
"uninstalling": "Désinstallation",
637637
"update": "Mettre à jour",
638+
"updateSelected": "Mettre à jour la sélection",
639+
"updateAll": "Tout mettre à jour",
638640
"updatingAllPacks": "Mise à jour de tous les paquets",
639641
"version": "Version"
640642
},

src/locales/ja/main.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,8 @@
635635
"uninstallSelected": "選択したものをアンインストール",
636636
"uninstalling": "アンインストール中",
637637
"update": "更新",
638+
"updateSelected": "選択を更新",
639+
"updateAll": "すべて更新",
638640
"updatingAllPacks": "すべてのパッケージを更新中",
639641
"version": "バージョン"
640642
},

src/locales/ko/main.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,8 @@
635635
"uninstallSelected": "선택 항목 제거",
636636
"uninstalling": "제거 중",
637637
"update": "업데이트",
638+
"updateSelected": "선택 항목 업데이트",
639+
"updateAll": "전체 업데이트",
638640
"updatingAllPacks": "모든 패키지 업데이트 중",
639641
"version": "버전"
640642
},

0 commit comments

Comments
 (0)