Skip to content

feat(content-uploader): Implement cancelled state uploads manager#4578

Merged
mergify[bot] merged 3 commits into
masterfrom
implement-cancelled-state-uploads-manager
May 28, 2026
Merged

feat(content-uploader): Implement cancelled state uploads manager#4578
mergify[bot] merged 3 commits into
masterfrom
implement-cancelled-state-uploads-manager

Conversation

@dealwith
Copy link
Copy Markdown
Collaborator

@dealwith dealwith commented May 20, 2026

Make STATUS_CANCELED a terminal state inside ContentUploader.updateViewAndCollection when the modernized flow is on. Without this, canceled items keep the view stuck in VIEW_UPLOAD_IN_PROGRESS and onComplete never fires for batches where every item was canceled

3/5 PR in the queue:

  1. feat(uploads-manager): integrate shared feature into ContentUploader #4573
  2. feat(content-uploader): Implement cancel all retry all handlers #4577
  3. 👉 feat(content-uploader): Implement cancelled state uploads manager #4578
  4. feat(content-uploader): Implement cancel all confirmation modal #4579
  5. feat(content-uploader): Implement permission error handling uploads #4580

Summary by CodeRabbit

  • Bug Fixes
    • Canceled uploads are now treated as finished so the uploader no longer appears to keep processing after cancellation.
    • Completion notifications only fire when at least one upload successfully completes, preventing false completion callbacks when all items were canceled.
  • Tests
    • Added tests verifying cancellation and completion behaviors across modernized and legacy flows.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 20, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 56578327-ee9c-48eb-b706-fd700d7c72f2

📥 Commits

Reviewing files that changed from the base of the PR and between 011434c and 1f66b97.

📒 Files selected for processing (2)
  • src/elements/content-uploader/ContentUploader.tsx
  • src/elements/content-uploader/__tests__/ContentUploader.test.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/elements/content-uploader/tests/ContentUploader.test.js
  • src/elements/content-uploader/ContentUploader.tsx

Walkthrough

Modernized upload completion now treats canceled items as terminal: in-progress checks ignore canceled items, finished checks include canceled, and onComplete(items) is only called if at least one item reached STATUS_COMPLETE; legacy behavior is preserved when the flag is off.

Changes

Completion Detection with Terminal Canceled Status

Layer / File(s) Summary
Modernized completion: terminal canceled and onComplete gating
src/elements/content-uploader/ContentUploader.tsx, src/elements/content-uploader/__tests__/ContentUploader.test.js
Adds a terminal predicate that treats STATUS_CANCELED as terminal in modernized mode, updates in-progress and finished computations accordingly, and gates onComplete(items) so it only fires if at least one item reached STATUS_COMPLETE. Tests cover canceled-only (no callback), mixed complete/canceled (callback fires), view resolution to VIEW_UPLOAD_SUCCESS with canceled items, partial-upload non-manager path behavior, and legacy-flag regression.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

ready-to-merge, queued

Suggested reviewers

  • jpan-box
  • olehrybak
  • dlasecki-box

Poem

🐰 When uploads hop and then decide to stop,
A canceled flag says that's the last crop.
If none complete, the bell stays still—no chime,
Quiet endings in modernized time. 🥕✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: implementing canceled state as a terminal state in the uploads manager when modernized uploads are enabled.
Description check ✅ Passed The description clearly explains the problem (canceled items stuck in progress, onComplete not firing) and solution (make STATUS_CANCELED terminal), with useful context about the PR queue.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch implement-cancelled-state-uploads-manager

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@dealwith dealwith changed the base branch from master to implement-cancel-all-retry-all-handlers May 21, 2026 09:36
olehrybak
olehrybak previously approved these changes May 27, 2026
Copy link
Copy Markdown
Contributor

@olehrybak olehrybak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good

@dealwith dealwith marked this pull request as ready for review May 27, 2026 14:25
@dealwith dealwith requested review from a team as code owners May 27, 2026 14:25
@dealwith dealwith changed the title Implement cancelled state uploads manager feat(content-uploader): Implement cancelled state uploads manager May 27, 2026
Comment thread src/elements/content-uploader/__tests__/ContentUploader.test.js Outdated
Comment thread src/elements/content-uploader/__tests__/ContentUploader.test.js Outdated
Comment thread src/elements/content-uploader/__tests__/ContentUploader.test.js Outdated
Comment thread src/elements/content-uploader/ContentUploader.tsx Outdated
@dealwith dealwith force-pushed the implement-cancel-all-retry-all-handlers branch from 90bbd3a to 4b36707 Compare May 28, 2026 12:15
@dealwith dealwith requested a review from a team as a code owner May 28, 2026 12:15
@dealwith dealwith force-pushed the implement-cancel-all-retry-all-handlers branch 2 times, most recently from 843f559 to 7947462 Compare May 28, 2026 15:54
Base automatically changed from implement-cancel-all-retry-all-handlers to master May 28, 2026 17:24
@mergify mergify Bot dismissed olehrybak’s stale review May 28, 2026 17:24

The base branch was changed.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/elements/content-uploader/__tests__/ContentUploader.test.js (1)

1199-1463: ⚡ Quick win

Remove the duplicated render()/enableModernizedUploads suite block.

Lines 1199-1463 re-declare the same suite already present earlier (Lines 809-1197). Keeping both doubles runtime and risks assertion drift between copies.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/elements/content-uploader/__tests__/ContentUploader.test.js` around lines
1199 - 1463, The test file contains a duplicated test suite: a second
describe('render()', () => { describe('enableModernizedUploads', ...) block
duplicates the earlier suite and should be removed; locate the duplicate block
that contains multiple tests referencing UploadsManager, UploadsManagerBP,
DroppableContent and methods like updateViewAndCollection,
handleCancelAllUploads, and handleRetryAllUploads, and delete that entire
duplicated describe block so only the original suite remains.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/elements/content-uploader/__tests__/ContentUploader.test.js`:
- Line 1093: The test creates a persistent spy with jest.spyOn(UploaderUtils,
'isMultiputSupported').mockReturnValue(true) in the
"handleUploadsManagerRetryAll should call onClickResume for resumable chunked
items" test but never restores it, causing leak into other tests; update that
test to call UploaderUtils.isMultiputSupported.mockRestore() in its own
finally/afterEach or restore the spy at the end of the test, or alternatively
use jest.spyOn(...).mockImplementationOnce(...) so the mock is scoped to that
single invocation; ensure you reference the spy created on
UploaderUtils.isMultiputSupported and the existing cleanup pattern used in the
getUploadAPI() describe (UploaderUtils.isMultiputSupported.mockRestore()) to
keep behavior isolated and prevent cross-test leakage.

---

Nitpick comments:
In `@src/elements/content-uploader/__tests__/ContentUploader.test.js`:
- Around line 1199-1463: The test file contains a duplicated test suite: a
second describe('render()', () => { describe('enableModernizedUploads', ...)
block duplicates the earlier suite and should be removed; locate the duplicate
block that contains multiple tests referencing UploadsManager, UploadsManagerBP,
DroppableContent and methods like updateViewAndCollection,
handleCancelAllUploads, and handleRetryAllUploads, and delete that entire
duplicated describe block so only the original suite remains.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 523d7142-a2f9-4799-8f71-6b1b387a1214

📥 Commits

Reviewing files that changed from the base of the PR and between 10ecc34 and c9536b2.

📒 Files selected for processing (6)
  • src/common/types/upload.js
  • src/constants.js
  • src/elements/content-uploader/ContentUploader.tsx
  • src/elements/content-uploader/__tests__/ContentUploader.test.js
  • src/elements/content-uploader/utils/__tests__/mapToModernizedUploadItem.test.ts
  • src/elements/content-uploader/utils/mapToModernizedUploadItem.ts

Comment thread src/elements/content-uploader/__tests__/ContentUploader.test.js
@dealwith dealwith force-pushed the implement-cancelled-state-uploads-manager branch from c9536b2 to c794314 Compare May 28, 2026 17:40
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (4)
src/elements/content-uploader/__tests__/ContentUploader.test.js (4)

1045-1058: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use STATUS_CANCELED constant instead of raw string.

Line 1055 uses status: 'canceled' instead of STATUS_CANCELED. Update to use the constant for consistency with the rest of the test file.

🔧 Proposed fix
             instance.updateViewAndCollection([
                 { status: STATUS_COMPLETE, file: { name: 'a' } },
-                { status: 'canceled', file: { name: 'b' } },
+                { status: STATUS_CANCELED, file: { name: 'b' } },
             ]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/elements/content-uploader/__tests__/ContentUploader.test.js` around lines
1045 - 1058, The test "updateViewAndCollection should fire onComplete when at
least one item completes (modernized)" uses a raw string 'canceled' for the
status; replace that with the STATUS_CANCELED constant used elsewhere in the
test file so the test uses the canonical symbol; update the object in the call
to instance.updateViewAndCollection (the second item's status) to
STATUS_CANCELED to match other tests and avoid fragile string usage.

1129-1157: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Restore the isMultiputSupported spy to prevent test leakage.

Line 1149 creates a spy with jest.spyOn(UploaderUtils, 'isMultiputSupported').mockReturnValue(true) but never restores it. The only mockRestore() for this spy is in the describe('getUploadAPI()') afterEach at line 553, which doesn't apply to this test.

Add cleanup to prevent the mock from leaking into other tests.

🔧 Proposed fix

Add restore after the test assertions:

                 expect(onClickResume).toHaveBeenCalledWith(resumable);
                 expect(resumable.bytesUploadedOnLastResume).toBe(1024);
+                UploaderUtils.isMultiputSupported.mockRestore();
             });

Or use mockReturnValueOnce to limit scope:

-                jest.spyOn(UploaderUtils, 'isMultiputSupported').mockReturnValue(true);
+                jest.spyOn(UploaderUtils, 'isMultiputSupported').mockReturnValueOnce(true);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/elements/content-uploader/__tests__/ContentUploader.test.js` around lines
1129 - 1157, The test "handleUploadsManagerRetryAll should call onClickResume
for resumable chunked items" creates a persistent spy on
UploaderUtils.isMultiputSupported which leaks into other tests; fix by either
calling mockRestore() on the spy after the assertions or replace
mockReturnValue(true) with mockReturnValueOnce(true) so the mock only affects
this test; locate the spy created via jest.spyOn(UploaderUtils,
'isMultiputSupported') in this test and add the appropriate cleanup
(mockRestore) or change to mockReturnValueOnce to limit scope.

1060-1071: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use STATUS_CANCELED constant instead of raw string.

Line 1068 uses status: 'canceled' instead of STATUS_CANCELED. Update to use the constant.

🔧 Proposed fix
             instance.updateViewAndCollection([
                 { status: STATUS_COMPLETE, file: { name: 'a' } },
-                { status: 'canceled', file: { name: 'b' } },
+                { status: STATUS_CANCELED, file: { name: 'b' } },
             ]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/elements/content-uploader/__tests__/ContentUploader.test.js` around lines
1060 - 1071, The test uses the raw string 'canceled' for an item's status;
change it to use the STATUS_CANCELED constant so the test stays in sync with the
codebase. In the test "updateViewAndCollection should treat canceled items as
terminal (modernized)" update the second item's status to STATUS_CANCELED and
ensure STATUS_CANCELED is available in the test scope (import it or reference it
from the same module where STATUS_COMPLETE is coming from) before running the
assertion against updateViewAndCollection.

1029-1043: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use STATUS_CANCELED constant instead of raw string.

Lines 1037 and 1039 use the raw string 'canceled' instead of the STATUS_CANCELED constant. The actual code uses STATUS_CANCELED, so tests should match for consistency and correctness. Change status: 'canceled' to status: STATUS_CANCELED.

🔧 Proposed fix
             const instance = wrapper.instance();
-            const canceled = { status: 'canceled' };
+            const canceled = { status: STATUS_CANCELED };
             instance.updateViewAndCollection([
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/elements/content-uploader/__tests__/ContentUploader.test.js` around lines
1029 - 1043, Replace raw 'canceled' status strings in the test with the
STATUS_CANCELED constant: in the test "updateViewAndCollection should not fire
onComplete when all items are canceled (modernized)" update the two objects that
set status: 'canceled' to status: STATUS_CANCELED so the test uses the same
constant as the implementation (locate the test around updateViewAndCollection
and the onComplete mock).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@src/elements/content-uploader/__tests__/ContentUploader.test.js`:
- Around line 1045-1058: The test "updateViewAndCollection should fire
onComplete when at least one item completes (modernized)" uses a raw string
'canceled' for the status; replace that with the STATUS_CANCELED constant used
elsewhere in the test file so the test uses the canonical symbol; update the
object in the call to instance.updateViewAndCollection (the second item's
status) to STATUS_CANCELED to match other tests and avoid fragile string usage.
- Around line 1129-1157: The test "handleUploadsManagerRetryAll should call
onClickResume for resumable chunked items" creates a persistent spy on
UploaderUtils.isMultiputSupported which leaks into other tests; fix by either
calling mockRestore() on the spy after the assertions or replace
mockReturnValue(true) with mockReturnValueOnce(true) so the mock only affects
this test; locate the spy created via jest.spyOn(UploaderUtils,
'isMultiputSupported') in this test and add the appropriate cleanup
(mockRestore) or change to mockReturnValueOnce to limit scope.
- Around line 1060-1071: The test uses the raw string 'canceled' for an item's
status; change it to use the STATUS_CANCELED constant so the test stays in sync
with the codebase. In the test "updateViewAndCollection should treat canceled
items as terminal (modernized)" update the second item's status to
STATUS_CANCELED and ensure STATUS_CANCELED is available in the test scope
(import it or reference it from the same module where STATUS_COMPLETE is coming
from) before running the assertion against updateViewAndCollection.
- Around line 1029-1043: Replace raw 'canceled' status strings in the test with
the STATUS_CANCELED constant: in the test "updateViewAndCollection should not
fire onComplete when all items are canceled (modernized)" update the two objects
that set status: 'canceled' to status: STATUS_CANCELED so the test uses the same
constant as the implementation (locate the test around updateViewAndCollection
and the onComplete mock).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 35b7f20d-9879-4c7b-b6b3-cb233f5b8a6b

📥 Commits

Reviewing files that changed from the base of the PR and between c9536b2 and c794314.

📒 Files selected for processing (2)
  • src/elements/content-uploader/ContentUploader.tsx
  • src/elements/content-uploader/__tests__/ContentUploader.test.js

Treat STATUS_CANCELED as terminal in updateViewAndCollection so the view
machine no longer reports canceled batches as in-progress. Suppress the
upload-success notification when every item in the batch was canceled.
Both behaviors gate on enableModernizedUploads; the legacy flow keeps
its previous logic.
@dealwith dealwith force-pushed the implement-cancelled-state-uploads-manager branch from c794314 to 011434c Compare May 28, 2026 19:02
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 28, 2026

Actionable comments posted: 0

…ndling

- Suppress onComplete on the partial-upload and non-manager paths when
  every item was canceled, matching the existing manager-path guard.
- Replace raw 'canceled' string literals with STATUS_CANCELED in tests.
- Group canceled-batch tests under a shared describe + beforeEach.
- Tighten view assertion to expect VIEW_UPLOAD_SUCCESS instead of just
  not VIEW_UPLOAD_IN_PROGRESS.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 28, 2026

Actionable comments posted: 0

@dealwith dealwith requested a review from jpan-box May 28, 2026 19:24
These keys belong to the Cancel All confirmation modal feature on a
later branch in the stack. Nothing in this PR consumes them, so they
should not land here.
Copy link
Copy Markdown
Collaborator

@jpan-box jpan-box left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All addressed — STATUS_CANCELED constant, positive view assertion, and shared test setup look good. Nice catch on the partial-upload guard from Rene too.

@dealwith dealwith self-assigned this May 28, 2026
@dealwith
Copy link
Copy Markdown
Collaborator Author

@Mergifyio queue

@mergify mergify Bot added the queued label May 28, 2026
@mergify
Copy link
Copy Markdown
Contributor

mergify Bot commented May 28, 2026

Merge Queue Status

  • Entered queue2026-05-28 20:07 UTC · Rule: Automatic strict merge
  • Checks skipped · PR is already up-to-date
  • Merged2026-05-28 20:09 UTC · at af1341deeb473ab73b41dd4177ded1d1dce59e5f · squash

This pull request spent 1 minute 12 seconds in the queue, including 8 seconds running CI.

Required conditions to merge

@mergify mergify Bot merged commit 7f02733 into master May 28, 2026
13 checks passed
@mergify mergify Bot deleted the implement-cancelled-state-uploads-manager branch May 28, 2026 20:09
@mergify
Copy link
Copy Markdown
Contributor

mergify Bot commented May 28, 2026

Merge Queue Status

  • 🟠 Waiting for queue conditions
  • ⏳ Enter queue
  • ⏳ Run checks
  • ⏳ Merge
Waiting for
  • -closed [📌 queue requirement]
All conditions
  • -closed [📌 queue requirement]
  • -conflict [📌 queue requirement]
  • -draft [📌 queue requirement]
  • any of [📌 queue -> configuration change requirements]:
    • -mergify-configuration-changed
    • check-success = Configuration changed
  • any of [🔀 queue conditions]:
    • all of [📌 queue conditions of queue rule Automatic strict merge]:
      • #approved-reviews-by >= 1 [🛡 GitHub branch protection]
      • #approved-reviews-by >= 2
      • #changes-requested-reviews-by = 0
      • #review-threads-unresolved = 0
      • #review-threads-unresolved = 0 [🛡 GitHub branch protection]
      • branch-protection-review-decision = APPROVED
      • github-review-decision = APPROVED [🛡 GitHub branch protection]
      • label = ready-to-merge
      • status-success = lint_test_build
      • title ~= ^(build|ci|chore|docs|feat|fix|perf|refactor|revert|style|test)(\([^)]+\))?:\s.+$
      • any of [🛡 GitHub branch protection]:
        • check-success = Summary
        • check-neutral = Summary
        • check-skipped = Summary
      • any of [🛡 GitHub branch protection]:
        • check-success = lint_test_build
        • check-neutral = lint_test_build
        • check-skipped = lint_test_build
      • any of [🛡 GitHub branch protection]:
        • check-success = license/cla
        • check-neutral = license/cla
        • check-skipped = license/cla
      • any of [🛡 GitHub branch protection]:
        • check-success = lint_pull_request
        • check-neutral = lint_pull_request
        • check-skipped = lint_pull_request
    • all of [📌 queue conditions of queue rule Automatic boxmoji merge]:
      • author = boxmoji
      • files ~= ^i18n/
      • title ~= ^(fix)\(i18n\)?:\supdate translations$
      • #approved-reviews-by >= 1 [🛡 GitHub branch protection]
      • #review-threads-unresolved = 0 [🛡 GitHub branch protection]
      • github-review-decision = APPROVED [🛡 GitHub branch protection]
      • status-success = lint_test_build
      • any of [🛡 GitHub branch protection]:
        • check-success = Summary
        • check-neutral = Summary
        • check-skipped = Summary
      • any of [🛡 GitHub branch protection]:
        • check-success = lint_test_build
        • check-neutral = lint_test_build
        • check-skipped = lint_test_build
      • any of [🛡 GitHub branch protection]:
        • check-success = license/cla
        • check-neutral = license/cla
        • check-skipped = license/cla
      • any of [🛡 GitHub branch protection]:
        • check-success = lint_pull_request
        • check-neutral = lint_pull_request
        • check-skipped = lint_pull_request

@mergify mergify Bot removed the queued label May 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants