-
Notifications
You must be signed in to change notification settings - Fork 472
feat: Stale-while-revalidate pattern for AssetBrowserModal #7880
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
82509e6
1432157
9629529
deaccc2
70c80a7
8517c3c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -63,12 +63,8 @@ | |
| </template> | ||
|
|
||
| <script setup lang="ts"> | ||
| import { | ||
| breakpointsTailwind, | ||
| useAsyncState, | ||
| useBreakpoints | ||
| } from '@vueuse/core' | ||
| import { computed, provide, watch } from 'vue' | ||
| import { breakpointsTailwind, useBreakpoints } from '@vueuse/core' | ||
| import { computed, provide } from 'vue' | ||
| import { useI18n } from 'vue-i18n' | ||
|
|
||
| import SearchBox from '@/components/common/SearchBox.vue' | ||
|
|
@@ -81,68 +77,68 @@ import type { AssetDisplayItem } from '@/platform/assets/composables/useAssetBro | |
| import { useAssetBrowser } from '@/platform/assets/composables/useAssetBrowser' | ||
| import { useModelUpload } from '@/platform/assets/composables/useModelUpload' | ||
| import type { AssetItem } from '@/platform/assets/schemas/assetSchema' | ||
| import { assetService } from '@/platform/assets/services/assetService' | ||
| import { formatCategoryLabel } from '@/platform/assets/utils/categoryLabel' | ||
| import { useAssetDownloadStore } from '@/stores/assetDownloadStore' | ||
| import { useAssetsStore } from '@/stores/assetsStore' | ||
| import { useModelToNodeStore } from '@/stores/modelToNodeStore' | ||
| import { OnCloseKey } from '@/types/widgetTypes' | ||
|
|
||
| const { t } = useI18n() | ||
| const assetStore = useAssetsStore() | ||
| const modelToNodeStore = useModelToNodeStore() | ||
| const breakpoints = useBreakpoints(breakpointsTailwind) | ||
|
|
||
| const props = defineProps<{ | ||
| nodeType?: string | ||
| assetType?: string | ||
| onSelect?: (asset: AssetItem) => void | ||
| onClose?: () => void | ||
| showLeftPanel?: boolean | ||
| title?: string | ||
| assetType?: string | ||
| }>() | ||
|
|
||
| const { t } = useI18n() | ||
|
|
||
| const emit = defineEmits<{ | ||
| 'asset-select': [asset: AssetDisplayItem] | ||
| close: [] | ||
| }>() | ||
|
|
||
| const breakpoints = useBreakpoints(breakpointsTailwind) | ||
|
|
||
| provide(OnCloseKey, props.onClose ?? (() => {})) | ||
|
|
||
| const fetchAssets = async () => { | ||
| // Compute the cache key based on nodeType or assetType | ||
| const cacheKey = computed(() => { | ||
| if (props.nodeType) return props.nodeType | ||
| if (props.assetType) return `tag:${props.assetType}` | ||
| return '' | ||
| }) | ||
|
|
||
| // Read directly from store cache - reactive to any store updates | ||
| const fetchedAssets = computed( | ||
| () => assetStore.modelAssetsByNodeType.get(cacheKey.value) ?? [] | ||
| ) | ||
|
|
||
| const isStoreLoading = computed( | ||
| () => assetStore.modelLoadingByNodeType.get(cacheKey.value) ?? false | ||
| ) | ||
|
|
||
| // Only show loading spinner when loading AND no cached data | ||
| const isLoading = computed( | ||
| () => isStoreLoading.value && fetchedAssets.value.length === 0 | ||
| ) | ||
|
|
||
| async function refreshAssets(): Promise<AssetItem[]> { | ||
| if (props.nodeType) { | ||
| return (await assetService.getAssetsForNodeType(props.nodeType)) ?? [] | ||
| return await assetStore.updateModelsForNodeType(props.nodeType) | ||
| } | ||
|
|
||
| if (props.assetType) { | ||
| return (await assetService.getAssetsByTag(props.assetType)) ?? [] | ||
| return await assetStore.updateModelsForTag(props.assetType) | ||
| } | ||
|
|
||
| return [] | ||
| } | ||
|
Comment on lines
+127
to
135
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial Consider handling empty cacheKey edge case. When neither 🤖 Prompt for AI Agents |
||
|
|
||
| const { | ||
| state: fetchedAssets, | ||
| isLoading, | ||
| execute | ||
| } = useAsyncState<AssetItem[]>(fetchAssets, [], { immediate: false }) | ||
|
|
||
| watch( | ||
| () => [props.nodeType, props.assetType], | ||
| async () => { | ||
| await execute() | ||
| }, | ||
| { immediate: true } | ||
| ) | ||
|
|
||
| const assetDownloadStore = useAssetDownloadStore() | ||
| // Trigger background refresh on mount | ||
| void refreshAssets() | ||
|
|
||
| watch( | ||
| () => assetDownloadStore.hasActiveDownloads, | ||
| async (currentlyActive, previouslyActive) => { | ||
| if (previouslyActive && !currentlyActive) { | ||
| await execute() | ||
| } | ||
| } | ||
| ) | ||
| const { isUploadButtonEnabled, showUploadDialog } = | ||
| useModelUpload(refreshAssets) | ||
|
|
||
| const { | ||
| searchQuery, | ||
|
|
@@ -153,8 +149,6 @@ const { | |
| updateFilters | ||
| } = useAssetBrowser(fetchedAssets) | ||
|
|
||
| const modelToNodeStore = useModelToNodeStore() | ||
|
|
||
| const primaryCategoryTag = computed(() => { | ||
| const assets = fetchedAssets.value ?? [] | ||
| const tagFromAssets = assets | ||
|
|
@@ -202,6 +196,4 @@ function handleAssetSelectAndEmit(asset: AssetDisplayItem) { | |
| // It handles the appropriate transformation (filename extraction or full asset) | ||
| props.onSelect?.(asset) | ||
| } | ||
|
|
||
| const { isUploadButtonEnabled, showUploadDialog } = useModelUpload(execute) | ||
| </script> | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Add tests to verify stale-while-revalidate behavior described in PR objectives.
The PR implements a "stale-while-revalidate pattern" where the loading spinner is shown only when loading AND no cached data exists. However, the current tests don't verify these key behaviors:
The test "displays cached assets immediately from store" (lines 240-251) verifies cached data is shown but doesn't confirm that
updateModelsForNodeTypeis still called for background refresh.📝 Suggested tests for stale-while-revalidate behavior
Based on learnings: Aim for behavioral coverage of critical and new features in unit tests.
🤖 Prompt for AI Agents