mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-02 19:49:58 +00:00
load assets browser before fetch completes and show loading state (#6189)
## Summary Moves the fetch and post-fetch logic associated with the asset browser into the component and shows a loading state while fetching. To test, use this branch: https://github.com/comfyanonymous/ComfyUI/pull/10045 https://github.com/user-attachments/assets/718974d5-efc7-46a0-bcd6-e82596d4c389 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-6189-load-assets-browser-before-fetch-completes-and-show-loading-state-2946d73d365081879d1bd05d86e8c036) by [Unito](https://www.unito.io) --------- Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
@@ -40,6 +40,7 @@
|
||||
<template #content>
|
||||
<AssetGrid
|
||||
:assets="filteredAssets"
|
||||
:loading="isLoading"
|
||||
@asset-select="handleAssetSelectAndEmit"
|
||||
/>
|
||||
</template>
|
||||
@@ -47,7 +48,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, provide } from 'vue'
|
||||
import { useAsyncState } from '@vueuse/core'
|
||||
import { computed, provide, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import SearchBox from '@/components/input/SearchBox.vue'
|
||||
import BaseModalLayout from '@/components/widget/layout/BaseModalLayout.vue'
|
||||
@@ -57,6 +60,9 @@ import AssetGrid from '@/platform/assets/components/AssetGrid.vue'
|
||||
import type { AssetDisplayItem } from '@/platform/assets/composables/useAssetBrowser'
|
||||
import { useAssetBrowser } from '@/platform/assets/composables/useAssetBrowser'
|
||||
import type { AssetItem } from '@/platform/assets/schemas/assetSchema'
|
||||
import { assetService } from '@/platform/assets/services/assetService'
|
||||
import { formatCategoryLabel } from '@/platform/assets/utils/categoryLabel'
|
||||
import { useModelToNodeStore } from '@/stores/modelToNodeStore'
|
||||
import { OnCloseKey } from '@/types/widgetTypes'
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -65,10 +71,12 @@ const props = defineProps<{
|
||||
onSelect?: (asset: AssetItem) => void
|
||||
onClose?: () => void
|
||||
showLeftPanel?: boolean
|
||||
assets?: AssetItem[]
|
||||
title?: string
|
||||
assetType?: string
|
||||
}>()
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'asset-select': [asset: AssetDisplayItem]
|
||||
close: []
|
||||
@@ -76,18 +84,73 @@ const emit = defineEmits<{
|
||||
|
||||
provide(OnCloseKey, props.onClose ?? (() => {}))
|
||||
|
||||
const fetchAssets = async () => {
|
||||
if (props.nodeType) {
|
||||
return (await assetService.getAssetsForNodeType(props.nodeType)) ?? []
|
||||
}
|
||||
|
||||
if (props.assetType) {
|
||||
return (await assetService.getAssetsByTag(props.assetType)) ?? []
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
|
||||
const {
|
||||
state: fetchedAssets,
|
||||
isLoading,
|
||||
execute
|
||||
} = useAsyncState<AssetItem[]>(fetchAssets, [], { immediate: false })
|
||||
|
||||
watch(
|
||||
() => [props.nodeType, props.assetType],
|
||||
async () => {
|
||||
await execute()
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
const {
|
||||
searchQuery,
|
||||
selectedCategory,
|
||||
availableCategories,
|
||||
contentTitle,
|
||||
categoryFilteredAssets,
|
||||
filteredAssets,
|
||||
updateFilters
|
||||
} = useAssetBrowser(props.assets)
|
||||
} = useAssetBrowser(fetchedAssets)
|
||||
|
||||
const modelToNodeStore = useModelToNodeStore()
|
||||
|
||||
const primaryCategoryTag = computed(() => {
|
||||
const assets = fetchedAssets.value ?? []
|
||||
const tagFromAssets = assets
|
||||
.map((asset) => asset.tags?.find((tag) => tag !== 'models'))
|
||||
.find((tag): tag is string => typeof tag === 'string' && tag.length > 0)
|
||||
|
||||
if (tagFromAssets) return tagFromAssets
|
||||
|
||||
if (props.nodeType) {
|
||||
const mapped = modelToNodeStore.getCategoryForNodeType(props.nodeType)
|
||||
if (mapped) return mapped
|
||||
}
|
||||
|
||||
if (props.assetType) return props.assetType
|
||||
|
||||
return 'models'
|
||||
})
|
||||
|
||||
const activeCategoryTag = computed(() => {
|
||||
if (selectedCategory.value !== 'all') {
|
||||
return selectedCategory.value
|
||||
}
|
||||
return primaryCategoryTag.value
|
||||
})
|
||||
|
||||
const displayTitle = computed(() => {
|
||||
return props.title ?? contentTitle.value
|
||||
if (props.title) return props.title
|
||||
|
||||
const label = formatCategoryLabel(activeCategoryTag.value)
|
||||
return t('assetBrowser.allCategory', { category: label })
|
||||
})
|
||||
|
||||
const shouldShowLeftPanel = computed(() => {
|
||||
|
||||
@@ -37,12 +37,12 @@
|
||||
<!-- Loading state -->
|
||||
<div
|
||||
v-if="loading"
|
||||
class="col-span-full flex items-center justify-center py-16"
|
||||
class="col-span-full flex items-center justify-center py-20"
|
||||
>
|
||||
<i
|
||||
class="icon-[lucide--loader]"
|
||||
:class="
|
||||
cn('size-6 animate-spin', 'text-stone-300 dark-theme:text-stone-200')
|
||||
cn('size-12 animate-spin', 'text-stone-300 dark-theme:text-stone-200')
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user