[CHIA-4324] NFT gallery: filter by preview availability - #3034
Open
jlobue10 wants to merge 6 commits into
Open
Conversation
CacheManager records the outcome of every download in a `-info` sidecar next to the cached file, but the renderer could only reach that state by asking for the content itself, which starts a download for anything not yet cached. A new read-only `getCacheInfos(urls)` IPC returns the persisted CacheInfo for a batch of URLs — CACHED with its checksum, the persisted ERROR, or NOT_CACHED for a URL never requested — without ever fetching. A URL the cache cannot key at all is reported as an ERROR entry for that URL instead of failing the whole batch. This lets the renderer classify NFTs it has not rendered from what earlier visits and sessions already learned about their files. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YKEGodEgVdEuvUM8dza52q
NFTs whose media cannot be shown — a dead host, a file that no longer matches its on-chain hash, or no file at all — render a placeholder tile but could not be singled out, so finding the broken ones in a large collection meant scrolling past every healthy one. The gallery's filter bar gains a third pill next to the Types and Visible/Hidden ones, with "Preview available" / "Preview not available" checkboxes and counts that follow the same four-state model as the visibility pill. The status behind the filter comes from two sources, both kept in a per-NFT store inside NFTProvider: - Tiles report what they actually settled on showing. The decision is derived from the same booleans NFTPreview renders from, so the filter classifies an NFT exactly as its tile does. Only preview-mode tiles report; the detail view verifies the full data file rather than the thumbnail and can legitimately disagree. - The gallery is virtualized, so most NFTs never mount. Those are classified from the cache's persisted outcomes via the new `getCacheInfos` IPC, mirroring the URI walk `useNFTVerifyHash` performs: a cached file matching the hash makes the preview available, and it is unavailable only once every URI has a settled failure. A URI the cache has never seen (or failed only transiently) leaves the NFT undecided, and undecided NFTs count as available — a tile would still attempt the download. Lookups run in batches of 200 URLs, once per NFT per session; a live report always wins over a lookup. Invalidating an NFT clears its verdict so the refreshed tile reports anew. Filtering and the statistics counts re-run as verdicts arrive, so with "Preview not available" selected the gallery converges on the broken NFTs as tiles settle. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YKEGodEgVdEuvUM8dza52q
… alone The cache lookup walked only the data URIs against the data hash, but a preview-mode tile verifies preview video, preview image, and data file in that order and shows the first verified one. An NFT whose thumbnail would verify while every data URI is a settled failure was therefore stored as unavailable, and under "Preview available" it stayed filtered out — never mounting, so no tile could ever correct it. The classifier now mirrors the tile: it takes the NFT's metadata state and walks the same sources in the same order; any verified source makes the preview available, and it is unavailable only once every URI of every source has a settled failure. Metadata that is still loading leaves the verdict undecided — a thumbnail may yet verify — so the store re-sweeps when metadata arrives, via a new global change event on the metadata store. The metadata store is already populated for every NFT by the gallery's search and statistics, so consulting it adds no requests. Sweeps are coalesced to one per 250ms and persisted outcomes are memoized per URL for the session, so the repeated sweeps during initial load only hit the main process for URLs not seen before. Invalidating an NFT forgets its URLs along with its verdict. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YKEGodEgVdEuvUM8dza52q
…er path Every preview-mode tile reported into the shared store, but the verdict was derived from what the tile happened to draw: a compact tile for a non-image type and a document or model tile show a type icon without opening the iframe, so a file that failed to download still counted as available there, while the gallery card for the same NFT hit prepareError and reported it unavailable. Whichever tile mounted last won, and an autocomplete row could flip the gallery's verdict. The verdict now follows the verification state all of these tiles share: the preview is available when the selected source is verified and could be served from the cache, unavailable when there is no file, a settled mismatch, or a download failure — the same outcome the hash badge reports, whatever the tile draws. It stays undecided while verification is in flight and, for a failed data file, while the metadata has not settled, since a thumbnail may still verify. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YKEGodEgVdEuvUM8dza52q
jlobue10
force-pushed
the
nft-6-preview-filter
branch
from
August 21, 2026 16:58
580b5b2 to
95d0f69
Compare
Moving the verdict reset into the refresh's finally block left the gallery classifying a refreshing NFT from what its files used to be for as long as the metadata fetch took — with a slow or hung metadata host, the whole timeout. The verdict is now dropped up front, together with the memoized outcomes of the data files, and dropped again once the deletions have completed: the preview uris are only known after the metadata round-trip, and a cache lookup that overlaps the deletions could memoize outcomes the refresh is about to remove. After the second reset the store holds nothing about the NFT, so the next lookup sees the files as not cached and only the refreshed tile's own verification decides. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YKEGodEgVdEuvUM8dza52q
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.
There are 3 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 862ce0c. Configure here.
Contributor
Author
|
Manual test done on my end: ran the branch (tip 862ce0c) in the GUI against my own collection. The new pill shows up next to Types and Visible / Hidden, unchecking "Preview available" narrows the gallery to the NFTs whose tiles show a placeholder, and the counts settle as the cache lookups and the tiles themselves report in. Working well. |
Two gaps around the refresh reset: - Waiting with Promise.all meant one rejected deletion (a uri the cache cannot key) threw before the late reset ran, and threw while the other deletions were still in flight. The refresh now waits for every deletion with allSettled, resets, and only then re-raises the first failure — so the reset can neither be skipped nor race a deletion. - A cache lookup whose IPC round-trip started before the files were deleted could return after the late reset, memoize CACHED outcomes for files that no longer exist, and settle the NFT on them; later sweeps then skipped it. The store now counts invalidations, and a lookup that spans one discards its result and starts the sweep over, since the NFTs the invalidation reset are unsettled again. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YKEGodEgVdEuvUM8dza52q
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.

A gallery filter for NFTs whose tile shows a placeholder instead of media — the "Preview is not available" / "File does not match the expected hash" / "No file available" tiles. Independent of the open #3029 / #3033: this branch is based directly on
mainand shares no commits with them.What this PR changes and why
Finding the broken NFTs in a large collection currently means scrolling past every healthy one. The gallery's filter bar gains a third pill next to Types and Visible / Hidden:
Where the status comes from
The tile decides what it shows only after it has verified the file, and the gallery is virtualized, so most NFTs never mount. The per-NFT status therefore has two sources, kept in a small store inside
NFTProvider(useNFTPreviewStatuses):NFTPreviewderives it from the verification state every preview-mode tile shares — available when the selected source is verified and could be served from the cache; unavailable for no file, a settled hash mismatch, or a download failure (the same outcome the hash badge reports, whatever the tile draws: iframe, type icon, or compact row). It stays undecided while verification is in flight and, for a failed data file, while the metadata has not settled. Only preview-mode tiles report; the detail view verifies the full data file rather than the thumbnail and can legitimately disagree.cacheAPI.getCacheInfos(urls)IPC that returns each URL's persistedCacheInfo(CACHED + checksum, the persisted ERROR, or NOT_CACHED) without ever downloading.getNFTPreviewStatusFromCachemirrors the walkuseNFTVerifyHashperforms for a preview-mode tile — preview video → preview image → data file, using the renderer's metadata store, which the gallery already populates for every NFT: any source with a cached file whose checksum matches its hash → available; unavailable only once every URI of every source has a settled failure (persisted download error, or cached bytes with the wrong checksum); a URI the cache has never seen — or failed only transiently (Request aborted/Response aborted, which the cache retries) — leaves the NFT undecided, as does metadata that is still loading (a thumbnail may yet verify; the store re-sweeps when it arrives). Sweeps are coalesced to one per 250 ms and run in batches of 200 URLs; persisted outcomes are memoized per URL for the session, so repeated sweeps only ask the main process about URLs not seen before.Design invariants:
Testing
getNFTPreviewStatusFromCacheunit tests: no file / no hash → unavailable; any matching cached URI → available (with and without the0xprefix); all URIs settled failures → unavailable; a never-fetched URI or a transient error → undecided; a verified thumbnail with a dead data file → available; a dead data file with metadata still loading → undecided, and unavailable only once the thumbnail has a settled failure too; the URL list the classifier consults (unhashed preview sources excluded).CacheManager.getCacheInfostest: reports CACHED / ERROR / NOT_CACHED per URL plus an ERROR entry for an un-keyable URL, anddownloadFileis never called.tscadds no new errors in touched files beyond the pre-existingChip size="extraSmall"pattern the neighbouring pills already have.🤖 Generated with Claude Code
https://claude.ai/code/session_01YKEGodEgVdEuvUM8dza52q
Note
Medium Risk
Touches NFT gallery filtering, NFTProvider state, and a new cache IPC path. Logic is carefully sequenced around invalidation, but incorrect classification could hide or miscount NFTs.
Overview
Adds an NFT gallery filter for tiles whose media is actually showable vs placeholder-only (no file, download failure, or hash mismatch). The new pill uses the same four-state checkbox model as visibility, with live counts.
Preview verdicts come from two sources without extra downloads: mounted preview tiles report after verification, and off-screen NFTs are classified from persisted cache state via a new read-only
getCacheInfosIPC. Undecided NFTs count as available until a settled failure; live tile reports win over cache lookups, and refresh clears the verdict.Reviewed by Cursor Bugbot for commit 9212010. Bugbot is set up for automated code reviews on this repo. Configure here.