Skip to content

feat(content-uploader): Implement cancel all retry all handlers#4577

Open
dealwith wants to merge 9 commits into
integrate-shared-feature-into-content-uploaderfrom
implement-cancel-all-retry-all-handlers
Open

feat(content-uploader): Implement cancel all retry all handlers#4577
dealwith wants to merge 9 commits into
integrate-shared-feature-into-content-uploaderfrom
implement-cancel-all-retry-all-handlers

Conversation

@dealwith
Copy link
Copy Markdown
Collaborator

@dealwith dealwith commented May 20, 2026

Summary

  • Implement Cancel All and Retry All for the modernized uploads manager: new handlers handleModernizedCancelAll and handleModernizedRetryAll wired to the panel's onCancelAll / onRetryAll.
  • Implement per-item Cancel and Retry: split the prior shared handleModernizedItemAction into dedicated handleModernizedItemCancel / handleModernizedItemRetry so each calls the right legacy callback (onClickCancel, onClickRetry/onClickResume) and respects status preconditions.
  • Extract markItemCanceled helper: cancels via api.cancel(), sets status to STATUS_CANCELED, fires onClickCancel. Shared by single-item and bulk cancel.
  • Add STATUS_CANCELED to constants and the 'canceled' status to the UploadStatus Flow union; extend mapToModernizedUploadItem STATUS_MAP with the canceled entry so canceled rows surface in the modernized panel.
  • Decouple UploadStatus from internal status constants — declare the union as string literals with a TODO to align with UploadItemStatus from @box/uploads-manager once the 'inprogress' value is migrated to 'uploading'.

2/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

dealwith added 4 commits May 20, 2026 11:28
Wire @box/uploads-manager UploadsManager into ContentUploader behind the
enableModernizedUploads flag. Maps legacy upload state to the shared
feature's item shape and delegates per-item cancel/retry/remove actions
to existing handlers.
Add STATUS_CANCELED constant. ContentUploader gains
handleCancelAllUploads, handleRetryAllUploads, plus per-item cancel and
retry handlers used by the modernized uploads manager. Canceled items
keep their entry in the list rather than being removed. All behavior is
gated on the enableModernizedUploads flag; the legacy flow is unchanged.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 20, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3eb4d4f0-1d70-4663-8756-aff95db99afd

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch implement-cancel-all-retry-all-handlers

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 integrate-shared-feature-into-content-uploader May 21, 2026 09:34
@dealwith dealwith force-pushed the integrate-shared-feature-into-content-uploader branch from b7bf9cc to 4acdc6a Compare May 25, 2026 13:50
dealwith added 3 commits May 25, 2026 18:35
…ntent-uploader' into implement-cancel-all-retry-all-handlers

# Conflicts:
#	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
@dealwith dealwith marked this pull request as ready for review May 26, 2026 13:58
@dealwith dealwith requested review from a team as code owners May 26, 2026 13:58
@dealwith dealwith changed the title Implement cancel all retry all handlers feat(content-uploader): Implement cancel all retry all handlers May 26, 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 and others added 2 commits May 27, 2026 16:50
… handlers

Mirror legacy onClick behavior in handleModernizedRetryAll and
handleModernizedItemRetry: drop ERROR_CODE_ITEM_NAME_IN_USE items
instead of looping on the same conflict, and fire onClickResume or
onClickRetry per item so consumer telemetry stays consistent with the
per-item flow. Snapshot the retry targets before iterating since
removeFileFromUploadQueue mutates itemsRef.current.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…retry flows

Cover Cancel All confirmation, Retry All on errored batches, and
single-item cancel/retry hover actions behind enableModernizedUploads.
useUploadsManager is required so the modernized panel actually expands.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
| typeof STATUS_STAGED
| typeof STATUS_COMPLETE
| typeof STATUS_ERROR;
// TODO: replace with `UploadItemStatus` from @box/uploads-manager once 'inprogress' is aligned to 'uploading'.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

i think we could add | typeof STATUS_CANCELED to the existing type instead of refactoring the whole union to inline strings. with inline strings the type and constants might drift. unless there is a strong reason to replace these all with inline strings..

i think adding 'canceled' is fine since that's additive. but the TODO mentions replacing 'inprogress' to 'uploading'. that would be a breaking change for consumers using useUploadsManager: true, since onComplete, onUpload, onClickCancel etc. all pass UploadItem with status directly to consumers. So i just want to make sure that's an "add" and not a "replace".

markItemCanceled = (item: UploadItem) => {
const { onClickCancel } = this.props;
const { api } = item;
if (api && typeof api.cancel === 'function') {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

i don't think we need the typeof api.cancel === 'function' check here. cancel is defined as a method on both upload API classes:

and api seems to always be constructed internally by ContentUploader so there's no path i can see where this would be something other than a function.

item => item.status === STATUS_PENDING || item.status === STATUS_IN_PROGRESS,
);
cancelable.forEach(item => this.markItemCanceled(item));
if (cancelable.length > 0) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

is this guard ever reachable? I woudl think UploadsManagerBP only renders the Cancel All button when hasActiveItems is true in the shared feature which means cancelable will always have items by the time this statement runs.

};

handleModernizedItemAction = (id: string) => {
handleModernizedItemCancel = (id: string) => {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

same feedback as #4573 on the naming. handleModernizedItemCancel reads like the item cancel is modernized, not that this is a cancel handler for the modernized component.

#4573 (comment)

name: 'b.pdf',
extension: 'pdf',
progress: 0,
status: 'canceled',
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

this uses the string 'canceled' while the rest of the test file uses imported constants (STATUS_ERROR, STATUS_COMPLETE, etc.). consider using STATUS_CANCELED for consistency.

),
];

export const modernizedSingleUpload = {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

the visual state of UploadsManagerBP should be covered by the shared feature's own tests. we're generally trying to reduce VRTs, so i'd leave it up to you on whether we'd lose any reliability if we removed or replaced these with unit tests

}
};

handleModernizedItemRetry = (id: string) => {
Copy link
Copy Markdown
Collaborator

@jpan-box jpan-box May 27, 2026

Choose a reason for hiding this comment

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

the retry logic here (name-in-use check, resumable vs restart) is duplicated from handleModernizedRetryAll. a shared helper that both call would make it easier to keep them in sync if the retry behavior changes.

// Name-in-use cannot be resolved by retrying — drop the item like the legacy onClick path.
if (error?.code === ERROR_CODE_ITEM_NAME_IN_USE) {
this.removeFileFromUploadQueue(item);
onClickCancel(item);
return;
}
const isChunkedUpload =
chunked && !item.isFolder && file.size > CHUNKED_UPLOAD_MIN_SIZE_BYTES && isMultiputSupported();
const isResumable = isResumableUploadsEnabled && isChunkedUpload && api?.sessionId;
if (isResumable) {
item.bytesUploadedOnLastResume = api.totalUploadedBytes;
this.resumeFile(item);
onClickResume(item);
} else {
this.resetFile(item);
this.uploadFile(item);
onClickRetry(item);
}
});

// Name-in-use cannot be resolved by retrying — drop the item like the legacy onClick path.
if (error?.code === ERROR_CODE_ITEM_NAME_IN_USE) {
this.removeFileFromUploadQueue(item);
onClickCancel(item);
return;
}
const isChunkedUpload =
chunked && !item.isFolder && file.size > CHUNKED_UPLOAD_MIN_SIZE_BYTES && isMultiputSupported();
const isResumable = isResumableUploadsEnabled && isChunkedUpload && api && api.sessionId;
if (isResumable) {
item.bytesUploadedOnLastResume = api.totalUploadedBytes;
this.resumeFile(item);
onClickResume(item);
} else {
this.resetFile(item);
this.uploadFile(item);
onClickRetry(item);
}
};

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.

great work and thank you for splitting up this feature into different pr, but there are some conventions that we really want to stick to, and also the naming modernized sticks out a lot to me.

const item = this.findItemByModernizedId(id);
if (item) {
this.onClick(item);
if (!item) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

same concern as #4573. if findItemByModernizedId doesn't find a match, this silently returns. we could catch any unhandled scenarios here with an else. not sure how you want to surface that though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants