Fix: Media upload from the post edit screen shows an inaccurate number when uploading multiple media files#11585
Fix: Media upload from the post edit screen shows an inaccurate number when uploading multiple media files#11585hbhalodia wants to merge 1 commit intoWordPress:trunkfrom
Conversation
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the Core Committers: Use this line as a base for the props when committing in SVN: To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
Test using WordPress PlaygroundThe changes in this pull request can previewed and tested using a WordPress Playground instance. WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser. Some things to be aware of
For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation. |
Trac ticket: https://core.trac.wordpress.org/ticket/65053
Use of AI Tools
AI assistance: Yes
Tool(s): GitHub Copilot, Claude
Model(s): Claude Sonnet 4.6
Used for: Identifying root cause and for a fix. Have reviwed the final implementation and raised the PR.
Detailed Claude Analysis and fix
Root Cause Analysis: "Showing X of Y media items" shows incorrect total count
Trac ticket: https://core.trac.wordpress.org/ticket/65053
Related PR: WordPress/gutenberg#77213
Affected files:
src/js/media/models/attachments.jsThe Symptom
When opening the media library modal (e.g. via "Set featured image" or an Image block), the footer media count displayed an incorrect total count:
The displayed count (
8) was correct, but the total (7) was one short of the real total.Background: How the count works
The media library uses a Backbone.js collection architecture with three key classes:
wp.media.model.QuerytotalAttachmentsparsed from theX-WP-Totalresponse header.wp.media.model.AttachmentsQueryand/or observe additional collections (e.g. the selection).wp.media.model.SelectionAttachments. Holds the currently selected items in the modal.The "Showing X of Y" text in the Load More bar is rendered by
AttachmentsBrowser.updateLoadMoreView():getTotalAttachments()reads directly from the mirroredQuerycollection'stotalAttachmentsproperty:The
totalAttachmentson theQueryis updated in real-time via two private methods bound as event listeners:The Root Cause
In
wp.media.model.Attachments, theobserve()method is responsible for watching another collection and replicating itsadd/remove/resetevents. Before this fix, it also registered the total-count trackers on every observed collection:The critical detail is that
observe()can be called on more than just the Query collection. Bothwp.media.controller.FeaturedImageandwp.media.controller.ReplaceImagecall:This is intentional and correct behaviour for display purposes — it ensures that a pre-existing featured image (which might not be in the first page of query results) is always visible in the grid even if the server hasn't returned it yet.
However, it also meant that the total-count handlers were now bound to the
Selectioncollection as well as theQuerycollection.The event cascade that causes the wrong count
library.observe(selection)is called. The selection contains 1 item (attachment 42).query.totalAttachments = 8. ✅Selection.add()is called for attachment 99. Becausethis.multiple === false, it first callsthis.remove(this.models)— removing attachment 42 from the selection.removeevent fires on theSelectioncollection._removeFromTotalAttachmentsis triggered (it was bound to the selection in step 2) →query.totalAttachmentsis decremented: 8 → 7. ❌Selection.add()completes, adding attachment 99, firingadd→_addToTotalAttachments→ 7 → 8. ✅ (briefly correct again)updateLoadMoreViewis debounced (10ms). If it fires between steps 7 and 8 (or if theaddevent is never fired due to validation), the UI shows "Showing 8 of 7".Additionally, in the general case, any
add/removeon the selection (without a correspondingadd/removeon the query) permanently corruptstotalAttachmentsfor the lifetime of the modal session.The Fix
The total-count event bindings (
_addToTotalAttachments/_removeFromTotalAttachments) belong exclusively on the primary query collection being mirrored — not on every additionally-observed collection.The fix moves these bindings from
observe()intomirror(), which is the method that establishes the relationship with theQuerycollection:Why this is safe
mirror()is only ever called with awp.media.model.Queryinstance (via_requery()). The query accurately reflects server-side counts, so attaching the total-count trackers there is correct.observe()is called for both the query (viamirror()) and supplemental collections like the selection. After this fix, observe() only handles item visibility/validation, which is its original purpose.unmirror()path is unaffected: it callsunobserve(this.mirroring)which calls.off(null, null, this)on the query, removing all handlers bound withthisas the context — including the total-count ones added inmirror().observe()in core that expect the total-count side effect.Files Changed
_addToTotalAttachments/_removeFromTotalAttachmentsbindings fromobserve()tomirror()Fix Location: Core, not Gutenberg
Although this bug is reproducible through the "Set featured image" panel in Gutenberg, the root cause is in the legacy media library JavaScript stack in WordPress Core. The
Attachments,Selection, andQueryBackbone models are part ofwp.mediawhich lives in Core (wp-includes/js/). Gutenberg'sMediaUploadcomponent is ultimately a wrapper that opens this same modal. Addingmultiple={false}toMediaUpload(as proposed in the Gutenberg PR #77213) would address the UX problem of allowing multi-select but would not fix this count bug, which is triggered by any single-selection change in the modal, including image blocks and other media contexts.This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.