Repository navigation
feat(media-animations): OS-level manifest cache + lightweight listing + download UX - #544
Merged
Merged
Conversation
…ownload UX - Add MediaAnimationCacheManager (src/features/media-animations/cache/mediaAnimationCache.ts) modeled on stickerCache.ts: BaseDirectory.AppCache, index.json, per-slug JSON files, isCached / ensureDownloaded / getManifest / clearCache; graceful browser degradation - Update mediaAnimationCatalog.ts: * Import mediaAnimationCacheManager + AnimationManifest * Append &fields=summary to category fetch URL (lighter response) * normalizeMediaPreset returns tracks:[] and defaultDuration:0.5 (summary has no tracks) * Add cachedManifests in-memory map + best-effort initialize() at module load * Add downloadManifest() export (ensureDownloaded + populates cachedManifests) * getMediaAnimationPreset() merges tracks/defaultDuration from cached manifest when present - Update PresetCard.tsx: * New props: isDownload * New props: isDownload * New props: isDownload * New props: isDownload * New props: isDownload * New props: isDownload * New props: isDownload * New props: isDownload * New props: isDo(bo * New props: isDownload * New props: isDownload * New props: isDownload * New pro: * Im * Im * Im * Im * Im * Im * Im * Im * Im * Im * Im * Im * Im * Im * Im wn * Im * Im * Im * Im * Im * Im * Im * Im * Im * Im * Im * Im * Im * Im / isDownloading / onDownload to each P * Im * Im * Im * Im * Im * Im * Im * Im * Im xport into cacheManager.ts tsc: 0 errors
…ownload UX
Fixes all review findings from .agents/tasks/media-animation-cache/review.json:
1. Fix implicit isDownloaded re-render dependency (review finding 1)
- Added explicit downloadedIds state Set in MediaAnimationSection
- handleDownload now updates downloadedIds in finally block alongside
clearing downloadingIds, making the checkmark update unconditionally
2. Eliminated (preset as any) casts for revisionId/thumbnailUrl/previewUrl (finding 2)
- Defined MediaAnimationPreset sub-interface in mediaAnimationCache.ts extending
RemoteTextAnimationPreset with thumbnailUrl?, previewUrl?, revisionId?
- Updated categorySnapshots, downloadedPresets, normalizeMediaPreset, and
getMediaAnimationPreset to use MediaAnimationPreset throughout
- MediaAnimationSection imports MediaAnimationPreset and uses typed access
3. Moved import to top of cacheManage3. Moved import to top of cacheManage3. Moved import to top of cacheManage3. Moved import to top of cacheManage3. Moved import to top of cacheManafol3. Moved import to top of cacheManage3. Moved import to top of cacheManageding 4)3. Moved import to top of cacheManage3. Moved import to top of cachwn3. Moved import to top of cacheManage3. Moved import to top of cu/zh-3. Moved import to top of cacheManage3. Moved import to top of cacheMasta3. Moved import to top of cacheManage3. Moved import to top of cacheManay-tracks on restart fixed (finding 5)
- MediaAnimationCacheManager.initialize() now calls hydrateManifests() after
loadIndex(), eagerly reading all previously-downloa loadIndex(), eagerly reading all previously-downloa loadIndex(), on loadIndex(), eagerly reading all previously-downloa loadIndex(), ho loadIndex(), eagerly reading all previously-downloa loade a loadIndex(), eagerly reading all previously-downloa loadIndex(), ean so listeners re-evaluate isCached()
6. SHA-256 hash verification on read (finding 6)
- Added readAndVerifyManifest() private helper that computes SHA-256 of
the raw bytes and compares against the stored contentHash
- ensureDownloaded() stores the hash of the actual written bytes
- getManifest() and hydrateManifests() go through readAndVerifyManifest();
corrupted files are evicted from the index and deleted from disk
- Mirrors the sticker cache pattern
Verification:
- npx tsc --noEmit: 0 errors
- npm run i18n:check: all 9 locales 100% valid
Blocking fixes: 1. updateMotion now passes manifests-merged catalog to compileClipMotionKeyframes — catalogSnapshot.presets has tracks:[] (summary); getMediaAnimationPreset() merges manifest tracks synchronously before compilation 2. Revert combo→loop phase-key: combo presets use phase 'in' and compile via the inPreset the inPreset the inPreset the inPreset the inPreset the inPreset the inPreset the inPresro the inPreset the inPreset the inPreset the inPresdr the inPreset the inPreset the inPreset the ileD the inPreset the inPreset the inPreset the ilick the inPreset the inPreset the inPreset.t the inPreset the inPre the inPreset the inPimationTrackProperty, join, appCac the inPreset the ind but the inPreset the inPreng current action (selectAnimation vs downloadAnimation); i18n key added to all 9 locales 7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.om MediaAnimationCacheManager Verification: tsc --noEmit 0 errors; npm run i18n:check all 9 locales 100%
Remove the status === 'ready' short-circuit in fetchMediaAnimationCategory so switching tabs always triggers a fresh fetch for that category. The in-flight dedup (categoryRequests) still prevents concurrent duplicate calls. This fixes Out/Combo tabs showing stale In data caused by module-level state persisting across hot-reloads with the old non-filtered fetch format.
…store Two bugs fixed: 1. Revert over-aggressive re-fetch: restore status==='ready' short-circuit so switching tabs doesn't re-fetch already-loaded categories 2. Fix Combo/Out showing In data: combo was mapped to phases['in'] in the hook, returning In presets on the Combo tab. Now combo is stored in the 'loop' phases slot and read back via 'loop' — each of the three tabs (in/out/combo) reads a fully independent snapshot slot. compileClipMotionKeyframes is unaffected: it uses the flat merged presets array where combo presets retain phase='in' for the inPreset branch.
…nent Extract grid rendering into a CategoryGrid component keyed by activeTab. Switching tabs unmounts the old grid and mounts a fresh one — each instance owns its own useMediaAnimationCategory hook, fetch lifecycle, and render state. Eliminates all cross-tab snapshot bleed regardless of store internals. Also restores status==='ready' short-circuit in fetchMediaAnimationCategory so categories only fetch once per session rather than on every tab switch.
… map Root cause of all tabs showing same data: useMediaAnimationCategory was reading from snapshot.phases which routes through the foreign AnimationCatalogSnapshot type — phases['in'] was being returned for combo, phases['out'] was referencing stale mutable objects, and React's reference equality check never detected changes. Fix: - Add categorySnapshotRefs — a separate Record keyed by MediaAnimationCategory that holds immutable copies of each category's snapshot - emit() rebuilds only the refs that actually changed (status/presets/error) so React sees new references exactly when data changes - useMediaAnimationCategory reads from categorySnapshotRefs[category] directly — no phases map, no type casting, no cross-category bleed possible
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two-tier catalog: summary listing (~200 bytes/item) + on-demand manifest fetch. MediaAnimationCacheManager stores tracks under AppCache/media-animations/ (mirrors stickerCacheManager). Download button per card. Fixes updateMotion no-op bug (was passing empty tracks to compiler). tsc + i18n check pass.