Skip to content

feat: add AttachmentCard component with generic async upload lifecycle#142

Open
itsprade wants to merge 3 commits intomainfrom
feature/attachment-card
Open

feat: add AttachmentCard component with generic async upload lifecycle#142
itsprade wants to merge 3 commits intomainfrom
feature/attachment-card

Conversation

@itsprade
Copy link
Copy Markdown
Contributor

@itsprade itsprade commented Mar 31, 2026

  • Resolve merge conflict between feature/attachment-card and origin/main
    • Conflict was in CLAUDE.md's Documentation section
    • main simplified the docs index to a single link to docs/; adopted that simplified approach
    • Merged origin/main into feature/attachment-card successfully

Introduce a reusable AttachmentCard API with image/file previews, menu actions, and optional async upload handling so products can implement upload transport externally while getting consistent loading and error UI.

Made-with: Cursor
@itsprade
Copy link
Copy Markdown
Contributor Author

/review

Copy link
Copy Markdown
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Generated by API Design Review for issue #142

}

const temporaryItems = files.map(createTemporaryUploadItem);
setLocalItems((prev) => [...temporaryItems.map((entry) => entry.item), ...prev]);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[3/4 — Medium] Object URL leak on component unmount during active upload

createTemporaryUploadItem creates a blob: URL via URL.createObjectURL for image previews. The finally block in handleUpload correctly revokes it after resolution — but only if the Promise settles while the component is still mounted. If the component unmounts mid-upload (e.g., user navigates away), the async Promise.all continues running in the background, setLocalItems calls trigger React warnings, and critically the finally block still runs — but there's nothing preventing further issues from state updates on the unmounted component.

The larger risk is if uploadFile never settles (e.g., hung request) — the blob URL is held indefinitely.

Fix: add an isMounted guard or AbortController signal, and revoke all pending object URLs on cleanup:

React.useEffect(() => {
  return () => {
    // Revoke any blob URLs still held by uploading items
    setLocalItems((prev) => {
      prev.filter(i => i.status === "uploading" && i.previewUrl?.startsWith("blob:"))
          .forEach(i => URL.revokeObjectURL(i.previewUrl!));
      return prev;
    });
  };
}, []);

Remove dead public upload fields, enforce mutually exclusive upload modes at the type level, and harden async object URL lifecycle cleanup to avoid unmount-related leaks while keeping behavior docs and tests in sync.

Made-with: Cursor
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Mar 31, 2026

Open in StackBlitz

npm i https://pkg.pr.new/@tailor-platform/app-shell@142
npm i https://pkg.pr.new/@tailor-platform/app-shell-vite-plugin@142

commit: e7d76d6

@itsprade
Copy link
Copy Markdown
Contributor Author

itsprade commented Apr 1, 2026

/review

@IzumiSy
Copy link
Copy Markdown
Contributor

IzumiSy commented Apr 6, 2026

@copilot fix the conflict

# Conflicts:
#	CLAUDE.md

Co-authored-by: IzumiSy <982850+IzumiSy@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 6, 2026

@copilot fix the conflict

Resolved in e7d76d6. The conflict in CLAUDE.md was due to origin/main simplifying the Documentation section to a single docs/ link while the branch had a detailed component list. Adopted the simplified approach from main.

Copilot AI requested a review from IzumiSy April 6, 2026 08:50
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