Skip to content

Conversation

mfts
Copy link
Owner

@mfts mfts commented Aug 3, 2025

Summary by CodeRabbit

  • New Features

    • Introduced image cropping functionality for logo and banner uploads on branding pages, allowing users to crop images to specific aspect ratios before saving.
    • Added cropping modals with live previews and controls to apply or cancel changes during image upload.
  • Chores

    • Added "react-image-crop" as a new dependency to support image cropping features.

Copy link

vercel bot commented Aug 3, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
papermark ✅ Ready (Inspect) Visit Preview 💬 Add feedback Aug 3, 2025 4:40pm

Copy link
Contributor

coderabbitai bot commented Aug 3, 2025

Walkthrough

A new image cropping feature has been implemented for logo and banner uploads in branding-related pages. The cropping UI uses the react-image-crop library, enforcing specific aspect ratios and allowing users to crop images before saving. Supporting state management, event handlers, and canvas rendering logic were added. The dependency was declared in package.json.

Changes

Cohort / File(s) Change Summary
Dependency Addition
package.json
Added "react-image-crop": "^11.0.10" to dependencies.
Branding Page Cropping
pages/branding.tsx
Integrated image cropping modal for logo uploads using react-image-crop, added state and logic for cropping, preview, and applying/canceling crop. Updated upload and drag-and-drop handlers to use cropping flow.
Dataroom Branding Cropping
pages/datarooms/[id]/branding/index.tsx
Added image cropping modals for both logo (1:1 aspect) and banner (6:1 aspect) uploads using react-image-crop. Implemented state management, cropping logic, canvas preview, and updated upload handlers for both image types.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI (Branding Page)
    participant ReactCrop
    participant Canvas
    participant State

    User->>UI (Branding Page): Uploads image (logo/banner)
    UI (Branding Page)->>State: Set imageToCrop, showCropper = true
    UI (Branding Page)->>ReactCrop: Render cropping modal with image
    User->>ReactCrop: Adjusts crop area, confirms crop
    ReactCrop->>Canvas: Render cropped area to canvas
    Canvas->>UI (Branding Page): Generate cropped image data URL
    UI (Branding Page)->>State: Update logo/banner with cropped image
    UI (Branding Page)->>UI (Branding Page): Hide cropping modal
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch cursor/add-image-cropping-to-branding-uploads-6a3f

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
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: 3

🧹 Nitpick comments (2)
pages/branding.tsx (1)

717-789: Well-implemented cropper modal

The modal UI is well-structured with proper z-indexing, responsive design, and clear user actions. The preview functionality provides good visual feedback.

Consider adding keyboard support for better UX:

 {showCropper && imageToCrop && (
-  <div className="fixed inset-0 z-50 bg-black/80 flex items-center justify-center p-4">
+  <div 
+    className="fixed inset-0 z-50 bg-black/80 flex items-center justify-center p-4"
+    onKeyDown={(e) => {
+      if (e.key === 'Escape') {
+        handleCropCancel();
+      }
+    }}
+    tabIndex={-1}
+  >
pages/datarooms/[id]/branding/index.tsx (1)

942-1090: Well-structured dual cropper modals

The separate modals for logo and banner cropping are well-implemented with appropriate aspect ratios and preview dimensions. The UI maintains consistency while accommodating the different requirements.

Similar to the branding.tsx suggestion, consider adding ESC key support for both modals to improve keyboard accessibility.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 24e260e and d611467.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (3)
  • package.json (1 hunks)
  • pages/branding.tsx (5 hunks)
  • pages/datarooms/[id]/branding/index.tsx (7 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Socket Security: Pull Request Alerts
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (3)
package.json (1)

119-119: LGTM!

The addition of react-image-crop dependency is appropriate for implementing the image cropping feature. Version ^11.0.10 allows for patch and minor updates which is reasonable for a UI library.

pages/branding.tsx (1)

143-151: Good UX improvement with input reset

Setting the image to crop and showing the cropper modal instead of directly setting the logo is the correct approach. The input value reset at line 150 is a nice touch that allows users to re-select the same file.

pages/datarooms/[id]/branding/index.tsx (1)

253-257: Appropriate banner aspect ratio configuration

The 6:1 aspect ratio for banners (matching the 1920x320 dimension requirement mentioned in the label) is correctly implemented.

Comment on lines +160 to +186
const handleCropComplete = useCallback(() => {
if (!previewCanvasRef.current) {
return;
}

previewCanvasRef.current.toBlob((blob) => {
if (!blob) {
toast.error("Failed to process image");
return;
}

const reader = new FileReader();
reader.onload = () => {
const dataUrl = reader.result as string;
setLogo(dataUrl);
// create a blob url for preview
const blob = convertDataUrlToFile({ dataUrl });
const blobUrl = URL.createObjectURL(blob);
setBlobUrl(blobUrl);
setShowCropper(false);
setImageToCrop("");
setCrop(undefined);
setCompletedCrop(undefined);
};
reader.readAsDataURL(blob);
});
}, []);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Potential memory leak with blob URLs

When creating a new blob URL at line 178, the previous blobUrl should be revoked to prevent memory leaks.

Add URL revocation before setting the new blob URL:

 const reader = new FileReader();
 reader.onload = () => {
   const dataUrl = reader.result as string;
   setLogo(dataUrl);
   // create a blob url for preview
   const blob = convertDataUrlToFile({ dataUrl });
+  // Revoke previous blob URL to prevent memory leak
+  if (blobUrl) {
+    URL.revokeObjectURL(blobUrl);
+  }
   const blobUrl = URL.createObjectURL(blob);
   setBlobUrl(blobUrl);
   setShowCropper(false);
   setImageToCrop("");
   setCrop(undefined);
   setCompletedCrop(undefined);
 };

Also consider adding cleanup in a useEffect:

useEffect(() => {
  return () => {
    if (blobUrl) {
      URL.revokeObjectURL(blobUrl);
    }
  };
}, [blobUrl]);
🤖 Prompt for AI Agents
In pages/branding.tsx around lines 160 to 186, the code creates a new blob URL
without revoking the previous one, which can cause memory leaks. To fix this,
before setting a new blob URL with setBlobUrl, check if there is an existing
blobUrl and call URL.revokeObjectURL on it to release the old URL. Additionally,
add a useEffect hook that cleans up by revoking the current blobUrl when the
component unmounts or when blobUrl changes, ensuring no blob URLs remain
allocated unnecessarily.

Comment on lines +218 to +244
const handleLogoCropComplete = useCallback(() => {
if (!logoPreviewCanvasRef.current) {
return;
}

logoPreviewCanvasRef.current.toBlob((blob) => {
if (!blob) {
toast.error("Failed to process image");
return;
}

const reader = new FileReader();
reader.onload = () => {
const dataUrl = reader.result as string;
setLogo(dataUrl);
// create a blob url for preview
const blob = convertDataUrlToFile({ dataUrl });
const blobUrl = URL.createObjectURL(blob);
setBlobUrl(blobUrl);
setShowLogoCropper(false);
setLogoImageToCrop("");
setLogoCrop(undefined);
setLogoCompletedCrop(undefined);
};
reader.readAsDataURL(blob);
});
}, []);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Memory leak with logo blob URL

Same issue as in branding.tsx - blob URLs should be revoked to prevent memory leaks.

 const reader = new FileReader();
 reader.onload = () => {
   const dataUrl = reader.result as string;
   setLogo(dataUrl);
   // create a blob url for preview
   const blob = convertDataUrlToFile({ dataUrl });
+  // Revoke previous blob URL to prevent memory leak
+  if (blobUrl) {
+    URL.revokeObjectURL(blobUrl);
+  }
   const blobUrl = URL.createObjectURL(blob);
   setBlobUrl(blobUrl);

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In pages/datarooms/[id]/branding/index.tsx around lines 218 to 244, the blob URL
created with URL.createObjectURL is not revoked, causing a memory leak. To fix
this, store the previous blob URL in a ref or state and call URL.revokeObjectURL
on it before setting a new blob URL. Also, ensure to revoke the blob URL when
the component unmounts to fully prevent memory leaks.

Comment on lines +259 to +285
const handleBannerCropComplete = useCallback(() => {
if (!bannerPreviewCanvasRef.current) {
return;
}

bannerPreviewCanvasRef.current.toBlob((blob) => {
if (!blob) {
toast.error("Failed to process image");
return;
}

const reader = new FileReader();
reader.onload = () => {
const dataUrl = reader.result as string;
setBanner(dataUrl);
// create a blob url for preview
const blob = convertDataUrlToFile({ dataUrl });
const bannerBlobUrl = URL.createObjectURL(blob);
setBannerBlobUrl(bannerBlobUrl);
setShowBannerCropper(false);
setBannerImageToCrop("");
setBannerCrop(undefined);
setBannerCompletedCrop(undefined);
};
reader.readAsDataURL(blob);
});
}, []);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Memory leak with banner blob URL

Banner blob URL should also be revoked to prevent memory leaks.

 const reader = new FileReader();
 reader.onload = () => {
   const dataUrl = reader.result as string;
   setBanner(dataUrl);
   // create a blob url for preview
   const blob = convertDataUrlToFile({ dataUrl });
+  // Revoke previous blob URL to prevent memory leak
+  if (bannerBlobUrl) {
+    URL.revokeObjectURL(bannerBlobUrl);
+  }
   const bannerBlobUrl = URL.createObjectURL(blob);
   setBannerBlobUrl(bannerBlobUrl);

Consider adding cleanup in useEffect for both URLs:

useEffect(() => {
  return () => {
    if (blobUrl) URL.revokeObjectURL(blobUrl);
    if (bannerBlobUrl) URL.revokeObjectURL(bannerBlobUrl);
  };
}, [blobUrl, bannerBlobUrl]);
🤖 Prompt for AI Agents
In pages/datarooms/[id]/branding/index.tsx around lines 259 to 285, the
bannerBlobUrl created with URL.createObjectURL is not revoked, causing a memory
leak. To fix this, add a useEffect hook that cleans up by calling
URL.revokeObjectURL on bannerBlobUrl (and any other blob URLs like blobUrl) when
the component unmounts or when these URLs change. This will properly release the
memory associated with the blob URLs.

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.

2 participants