Fix Recycle Bin 'Empty bin' truncation past 200 items - #101
Merged
Conversation
The server caps `desktop_mode_recycle_bin_empty()` at one chunk per call so PHP doesn't time out on huge bins. The client was calling the endpoint exactly once and treating the result as final, so on a 250-item bin the user saw "emptied" with 50 items still in the bin. * Keep the server cap, but make it filterable via `desktop_mode_recycle_bin_empty_chunk_size` (default 200) for sites with larger PHP execution budgets. * Move the iteration to the client. New `runEmptyLoop()` driver calls the endpoint until `remaining` hits zero, bails when no progress is possible (every leftover item capability-blocked), and surfaces progress to the Empty bin button so the user sees "Emptying... 200 of 500" instead of a frozen UI. * Add PHP coverage for the chunk cap, the iterate-to-empty pattern, and the new filter. * Add vitest coverage for the loop driver: completion, early-exit on partial-skip, and the iteration-cap guard. * Document the new filter in `docs/hooks-reference.md`. Fixes #97
Contributor
✅ WordPress Plugin Check Report
📊 ReportAll checks passed! No errors or warnings found. 🤖 Generated by WordPress Plugin Check Action • Learn more about Plugin Check |
Two related fixes so the recycle-bin dock badge updates the moment the bin is drained, instead of lingering with the pre-empty count until the post-empty refresh round-trip lands. 1. badge.ts: route module-level state through createSharedStore. `_current`, `_seenTs`, `_started`, and `_countUrl` were plain `let`s. The module is imported by both the always-on shell bundle (`desktop.js`) and the lazy bin bundle (`recycle-bin.js`), so each bundle compiled its own copy and mutations from one were invisible to the other. The bin window's `setRecycleBinBadge` calls would update the bin bundle's `_current`, while the desktop bundle's WINDOW_CLOSED lifecycle handler would repaint the dock from its own stale `_current`. AGENTS.md's "Cross-bundle state" section flags this exact pattern as non-negotiable. 2. recycle-bin/index.ts: optimistic zero on full empty. `handleEmpty` now calls `setRecycleBinBadge(0)` as soon as `runEmptyLoop` reports `stoppedBecause: 'empty'`, before the subsequent `refresh()` reconciles authoritatively. Removes the visible lag during the post-empty REST round-trip.
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.
Summary
desktop_mode_recycle_bin_empty()previously fetched one page of 200 items and returned success even when more items remained. Users with a >200-item bin saw a "success" toast next to a still-non-empty bin.The 200-item per-call cap is intentional (PHP timeout protection on huge bins), so the fix keeps the cap but makes it filterable, has the client iterate until done, and surfaces progress to the user.
Changes
Server (
includes/recycle-bin/store.php)desktop_mode_recycle_bin_empty()now reads its chunk cap via the newdesktop_mode_recycle_bin_empty_chunk_sizefilter (default 200, floored to 1).Client (new
src/recycle-bin/empty-loop.ts+ updates tosrc/recycle-bin/index.ts)runEmptyLoop()driver that calls the server untilremaining === 0, bails on no-progress (purged === 0 && skipped > 0means every leftover is capability-blocked), with a 1000-iteration safety ceiling.handleEmpty()now delegates torunEmptyLoop. While running, the Empty bin button getsdisabled+aria-busyand its label cycles "Emptying..." then "Emptying... N of M". Slotted dashicon preserved by mutating only a trailing span.Tests
tests/phpunit/tests/recycleBinStore.php(new): 250-item case (single call leavesremaining > 0), iterate-to-empty case, filter honored, floor-to-1.tests/vitest/recycle-bin-empty-loop.test.ts(new): 250-item-with-200-chunk, single-call completion, progress callback ordering, no-progress bail, partial-skip iteration, iteration-cap guard.Docs
docs/hooks-reference.md: new filter entry next to the other recycle-bin filters.Verified
npm run buildcleannpm run lintcleantsc --noEmitcleannpm run test:js: 88 files / 809 tests pass (includes 6 new vitest cases)Open question
Used
@since 0.21.1for the new filter to match the aspirational versioning already instore.php(the file uses 0.19.0/0.21.0 even though the plugin is at 0.7.2). Easy to change if a different label is preferred.Closes #97