Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
13 changes: 11 additions & 2 deletions ui/src/features/asset-picker/remoteCatalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ import type { AssetConstraints, CatalogSort, PickerItem } from './types.ts'

const DEFAULT_KINDS: AssetKind[] = ['image', 'video', 'audio', 'model3d', 'scene']

export function remoteCatalogFilterKey(
kind: AssetKind | '',
constraints?: AssetConstraints,
): string {
if (kind) return kind
return (constraints?.kinds || []).join(',')
}

export function useRemoteCatalogPage(input: {
enabled: boolean
workspaceId?: string
Expand All @@ -28,10 +36,11 @@ export function useRemoteCatalogPage(input: {
const [items, setItems] = useState<PickerItem[]>([])
const [total, setTotal] = useState(0)
const [status, setStatus] = useState<'ready' | 'loading' | 'error'>(input.enabled ? 'loading' : 'ready')
const kindsKey = remoteCatalogFilterKey(input.kind, input.constraints)
useEffect(() => () => session.current.dispose(), [])
useEffect(() => {
if (!input.enabled || !input.workspaceId) return
const kinds = input.kind ? [input.kind] : input.constraints?.kinds
const kinds = kindsKey ? kindsKey.split(',') as AssetKind[] : undefined
void session.current.run({
workspace: input.workspaceId,
search: input.query.trim() || undefined,
Expand All @@ -45,7 +54,7 @@ export function useRemoteCatalogPage(input: {
setTotal(result.total)
setStatus('ready')
}).catch(() => setStatus('error'))
}, [input.constraints, input.enabled, input.kind, input.page, input.query, input.retry, input.sort, input.workspaceId])
}, [input.enabled, input.page, input.query, input.retry, input.sort, input.workspaceId, kindsKey])
return { items, total, status }
}

Expand Down
47 changes: 47 additions & 0 deletions ui/tests/assetExplorerRemote.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,50 @@ test('remote explorer pages the catalog at 24 and can find a hit past the first
cleanup()
}
})

test('remote explorer does not refetch when the parent rebuilds the same constraints object', { concurrency: false }, async () => {
const { render, screen, waitFor, cleanup } = await import('@testing-library/react')
const { AssetExplorerDialog } = await import('../src/components/common/AssetExplorerDialog.tsx')
const calls: string[] = []
const originalFetch = globalThis.fetch
globalThis.fetch = (async (input: RequestInfo | URL) => {
calls.push(String(input))
return new Response(JSON.stringify({
total: 1,
assets: [catalogAsset(0, 'clip.mp4')],
}), { status: 200, headers: { 'Content-Type': 'application/json' } })
}) as typeof fetch
try {
const view = render(
<AssetExplorerDialog
open
remote
workspaceId="film"
title="Library"
items={[]}
constraints={{ kinds: ['video'], maxCount: 1, optional: false }}
onClose={() => undefined}
onChoose={() => undefined}
/>,
)
await waitFor(() => assert.ok(screen.getByTitle('clip.mp4')))
assert.equal(calls.length, 1)
view.rerender(
<AssetExplorerDialog
open
remote
workspaceId="film"
title="Library"
items={[]}
constraints={{ kinds: ['video'], maxCount: 1, optional: false }}
onClose={() => undefined}
onChoose={() => undefined}
/>,
)
await waitFor(() => assert.ok(screen.getByTitle('clip.mp4')))
assert.equal(calls.length, 1)
} finally {
globalThis.fetch = originalFetch
cleanup()
}
})
10 changes: 10 additions & 0 deletions ui/tests/remoteCatalog.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
explorerCanConfirm,
explorerListModel,
explorerToolbarKinds,
remoteCatalogFilterKey,
remotePageCount,
resolveExplorerSelection,
} from '../src/features/asset-picker/remoteCatalog.ts'
Expand Down Expand Up @@ -39,6 +40,15 @@ function outputFrom(item: PickerItem): ApiOutput {
}
}

test('catalog filter key ignores constraints object identity', () => {
const first = { kinds: ['video'] as const, maxCount: 1, optional: false }
const second = { kinds: ['video'] as const, maxCount: 1, optional: false }
assert.equal(remoteCatalogFilterKey('', first), 'video')
assert.equal(remoteCatalogFilterKey('', second), remoteCatalogFilterKey('', first))
assert.equal(remoteCatalogFilterKey('audio', first), 'audio')
assert.equal(remoteCatalogFilterKey('', undefined), '')
})

test('remote pages are 24 wide and never collapse to zero', () => {
assert.equal(ASSET_PICKER_PAGE_SIZE, 24)
assert.equal(remotePageCount(0), 1)
Expand Down
Loading