Skip to content

[WIKI-419] chore: new asset duplicate endpoint added#7172

Merged
sriramveeraghanta merged 45 commits intopreviewfrom
chore/asset-duplicate-endpoint
Nov 20, 2025
Merged

[WIKI-419] chore: new asset duplicate endpoint added#7172
sriramveeraghanta merged 45 commits intopreviewfrom
chore/asset-duplicate-endpoint

Conversation

@aaryan610
Copy link
Copy Markdown
Member

@aaryan610 aaryan610 commented Jun 5, 2025

Description

This PR introduces a new endpoint to duplicate assets and attach them to an entiyt without the need to re-upload them.

Type of Change

  • Improvement (change that would cause existing functionality to not work as expected)

Summary by CodeRabbit

  • New Features
    • Duplicate file assets via a new API endpoint and receive a new asset ID.
    • Per-asset rate limiting to curb excessive duplicate requests.
    • Editor support to duplicate embedded assets (rich text/images), including retry UI and failure messaging.
    • Paste handling now processes and deduplicates embedded assets; editor no longer strips img tags on paste.
  • Bug Fixes
    • None.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 5, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds a workspace-scoped POST endpoint to duplicate uploaded assets (per-asset throttled), server logic to copy storage and create a new FileAsset, client-side service/store/hook wiring to call it, editor file-handler duplicate support, paste-time duplication handlers, and custom-image duplication lifecycle and UI retry controls.

Changes

Cohort / File(s) Change Summary
API route & export
apps/api/plane/app/urls/asset.py, apps/api/plane/app/views/__init__.py
Register DuplicateAssetEndpoint route at assets/v2/workspaces/<str:slug>/duplicate-assets/<uuid:asset_id>/ and export the endpoint from views.
API implementation & throttling
apps/api/plane/app/views/asset/v2.py, apps/api/plane/throttles/asset.py, apps/api/plane/settings/common.py
Add DuplicateAssetEndpoint (POST) that validates inputs, maps entity context to FK, copies storage object to a new key, creates a new FileAsset (uploaded=true) and returns new asset id; add AssetRateThrottle keyed by asset_id and register asset_id = 5/minute (and anon = 30/minute).
Web client — service & store
apps/web/core/services/file.service.ts, apps/web/core/store/editor/asset.store.ts, apps/web/core/hooks/store/use-editor-asset
Add fileService.duplicateAsset(...); expose duplicateEditorAsset in store/hook that calls the service and returns { asset_id }.
Web UI integrations
apps/web/core/components/**, apps/web/core/components/editor/rich-text/description-input/root.tsx, apps/web/core/components/inbox/modals/create-modal/issue-description.tsx, apps/web/core/components/issues/issue-detail/issue-activity/helper.tsx, apps/web/core/components/issues/issue-modal/components/description-editor.tsx, apps/web/app/.../page.tsx
Wire duplicateEditorAsset into editor/file handlers and RichTextEditor via new duplicateFile prop; handlers call duplication with workspace/project/entity params, propagate returned asset id, and surface consistent error messages on failure.
Editor paste plugin & helpers
packages/editor/src/ce/helpers/asset-duplication.ts, packages/editor/src/core/plugins/paste-asset.ts, packages/editor/src/core/extensions/utility.ts, packages/editor/src/core/props.ts
Add asset-duplication handlers (image handler injecting new UUID and STATUS=DUPLICATING), add PasteAssetPlugin to process pasted HTML via handlers and re-dispatch modified paste, register plugin in utility extension, and remove prior transformPastedHTML hook.
Custom-image types, utils, extension
packages/editor/src/core/extensions/custom-image/types.ts, .../utils.ts, .../extension.tsx
Add ECustomImageStatus enum (PENDING, UPLOADING, UPLOADED, DUPLICATING, DUPLICATION_FAILED), include STATUS in defaults, add helpers (isImageDuplicating, hasImageDuplicationFailed, etc.), and expose optional duplicateImage option from fileHandler.
Custom-image components (node-view / uploader / block)
packages/editor/src/core/extensions/custom-image/components/node-view.tsx, .../uploader.tsx, .../block.tsx
Integrate duplication lifecycle: node-view triggers duplicateImage when status is DUPLICATING and updates attrs or sets DUPLICATION_FAILED; uploader accepts hasDuplicationFailed, shows Retry UI and blocks picker while failed; block component adjusts loader/toolbar/resizer visibility during duplication.
Editor types & props
packages/editor/src/core/types/config.ts, packages/editor/src/core/props.ts, packages/editor/src/core/extensions/utility.ts, packages/editor/src/core/plugins/paste-asset.ts
Add duplicate(assetId: string) => Promise<string> to TFileHandler; remove transformPastedHTML from CoreEditorProps; register PasteAssetPlugin() in utility plugins.
Site app fallback
apps/space/helpers/editor.helper.ts
Add a no-op duplicate(assetId) handler returning the same id as fallback (marked unsupported for sites/space).

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Editor as Editor UI
    participant Store as EditorStore / FileService
    participant API as DuplicateAssetEndpoint
    participant Throttle as AssetRateThrottle
    participant DB as FileAsset DB
    participant Storage as ObjectStorage

    Editor->>Store: duplicateEditorAsset(assetId, entityType, ...)
    Store->>API: POST /assets/v2/workspaces/{slug}/duplicate-assets/{assetId}/ { entity_type, entity_id?, project_id? }
    API->>Throttle: get_cache_key(view.kwargs) -> "throttle_asset_{assetId}"
    Throttle-->>API: allow/deny
    API->>DB: GET original FileAsset(assetId)
    API->>Storage: copy original_key -> new_dest_key
    API->>DB: CREATE FileAsset (uploaded=true, linked FK)
    API-->>Store: 201 { asset_id: new_asset_id }
    Store-->>Editor: return { asset_id: new_asset_id }
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas needing extra attention:
    • Storage copy semantics and chosen destination key naming in DuplicateAssetEndpoint.post.
    • Correctness of entity-type → FK mapping in get_entity_id_field.
    • Throttle key logic in AssetRateThrottle.get_cache_key and matching DEFAULT_THROTTLE_RATES key.
    • Paste plugin HTML mutation, idempotency marking (data-uploaded) and re-dispatch behavior.
    • Concurrency guards and retry transitions in custom-image node-view/uploader (DUPLICATING → UPLOADED / DUPLICATION_FAILED).
    • Client-side error propagation and consistent UI messages across duplicate flows.

Poem

I’m a rabbit with a tiny hat,
I clone pixels where they sat.
Keys get twins and paste gets wise,
Throttle hums and small retries.
Hop again — the duplicate flies! 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning The description is incomplete. While it mentions the feature (duplicating assets), it lacks required sections like Test Scenarios, Screenshots/Media, and References, and contains a typo ('entiyt'). Add Test Scenarios section describing how the duplication feature was tested, include any relevant Screenshots/Media, and add References linking to the related issue (WIKI-419). Fix the typo 'entiyt' to 'entity'.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding a new asset duplicate endpoint, which is the core feature across all modified files.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/asset-duplicate-endpoint

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.

@makeplane
Copy link
Copy Markdown

makeplane Bot commented Jun 5, 2025

Linked to Plane Work Item(s)

This comment was auto-generated by Plane

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: 5

🔭 Outside diff range comments (1)
web/core/services/file.service.ts (1)

36-45: 🛠️ Refactor suggestion

Remove duplicate enum definition.

The local TFileAssetType enum duplicates the imported EFileAssetType enum. This creates unnecessary duplication and potential maintenance issues.

Since EFileAssetType is now imported from @plane/types, remove the local enum definition and use the imported one consistently throughout the codebase.

- export enum TFileAssetType {
-   COMMENT_DESCRIPTION = "COMMENT_DESCRIPTION",
-   ISSUE_ATTACHMENT = "ISSUE_ATTACHMENT",
-   ISSUE_DESCRIPTION = "ISSUE_DESCRIPTION",
-   PAGE_DESCRIPTION = "PAGE_DESCRIPTION",
-   PROJECT_COVER = "PROJECT_COVER",
-   USER_AVATAR = "USER_AVATAR",
-   USER_COVER = "USER_COVER",
-   WORKSPACE_LOGO = "WORKSPACE_LOGO",
- }
🧹 Nitpick comments (1)
apiserver/plane/app/views/asset/v2.py (1)

783-783: Fix line length violation.

The line exceeds the maximum length limit (106 > 88 characters) as flagged by static analysis.

Break the long line for better readability:

- destination_key = f"{workspace.id}/{uuid.uuid4().hex}-{original_asset.attributes.get('name')}"
+ destination_key = (
+     f"{workspace.id}/{uuid.uuid4().hex}-"
+     f"{original_asset.attributes.get('name')}"
+ )
🧰 Tools
🪛 Ruff (0.11.9)

783-783: Line too long (106 > 88)

(E501)

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 986f29d and d52cce1.

📒 Files selected for processing (4)
  • apiserver/plane/app/urls/asset.py (2 hunks)
  • apiserver/plane/app/views/__init__.py (1 hunks)
  • apiserver/plane/app/views/asset/v2.py (1 hunks)
  • web/core/services/file.service.ts (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
apiserver/plane/app/urls/asset.py (1)
apiserver/plane/app/views/asset/v2.py (1)
  • DuplicateAssetEndpoint (723-810)
🪛 Ruff (0.11.9)
apiserver/plane/app/views/asset/v2.py

783-783: Line too long (106 > 88)

(E501)

🪛 Pylint (3.3.7)
apiserver/plane/app/views/asset/v2.py

[refactor] 724-724: Too many return statements (7/6)

(R0911)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (5)
apiserver/plane/app/views/__init__.py (1)

110-110: LGTM!

The import addition is correctly placed and follows the existing pattern for asset v2 endpoints.

apiserver/plane/app/urls/asset.py (1)

16-16: LGTM!

The import addition follows the existing pattern and is correctly placed.

web/core/services/file.service.ts (2)

4-4: Good import addition.

The import of EFileAssetType from the types package is the correct approach for type consistency.


262-276: LGTM!

The duplicateAssets method is well-implemented with:

  • Correct parameter types and return type
  • Proper endpoint URL construction
  • Consistent error handling pattern
  • Clear method signature that matches the backend API
apiserver/plane/app/views/asset/v2.py (1)

804-808: LGTM!

The bulk update to set is_uploaded=True for all duplicated assets is efficient and correct. The conditional check ensures the update only happens when there are successfully duplicated assets.

Comment thread apiserver/plane/app/urls/asset.py
Comment thread apiserver/plane/app/views/asset/v2.py Outdated
Comment thread apiserver/plane/app/views/asset/v2.py
Comment thread apiserver/plane/app/views/asset/v2.py Outdated
Comment thread apiserver/plane/app/views/asset/v2.py
Comment thread apiserver/plane/app/views/asset/v2.py
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)
apiserver/plane/app/views/asset/v2.py (4)

728-759: 🛠️ Refactor suggestion

Extract duplicated method to reduce code duplication.

This method is duplicated across multiple endpoint classes (WorkspaceFileAssetEndpoint, ProjectAssetEndpoint, and now DuplicateAssetEndpoint), violating the DRY principle.

Extract this method to the BaseAPIView class or create a mixin. Additionally, refactor to use a dictionary mapping to reduce the number of return statements:

def get_entity_id_field(self, entity_type, entity_id):
    entity_field_mapping = {
        FileAsset.EntityTypeContext.WORKSPACE_LOGO: {"workspace_id": entity_id},
        FileAsset.EntityTypeContext.PROJECT_COVER: {"project_id": entity_id},
        FileAsset.EntityTypeContext.USER_AVATAR: {"user_id": entity_id},
        FileAsset.EntityTypeContext.USER_COVER: {"user_id": entity_id},
        FileAsset.EntityTypeContext.ISSUE_ATTACHMENT: {"issue_id": entity_id},
        FileAsset.EntityTypeContext.ISSUE_DESCRIPTION: {"issue_id": entity_id},
        FileAsset.EntityTypeContext.PAGE_DESCRIPTION: {"page_id": entity_id},
        FileAsset.EntityTypeContext.COMMENT_DESCRIPTION: {"comment_id": entity_id},
    }
    return entity_field_mapping.get(entity_type, {})
🧰 Tools
🪛 Pylint (3.3.7)

[refactor] 728-728: Too many return statements (7/6)

(R0911)


761-767: 🛠️ Refactor suggestion

Add validation for entity_type parameter.

The entity_type parameter is retrieved but not validated against allowed values, which could lead to invalid data being stored.

Add validation after line 766:

entity_type = request.data.get("entity_type", None)
+ 
+ if entity_type and entity_type not in FileAsset.EntityTypeContext.values:
+     return Response(
+         {"error": "Invalid entity type.", "status": False},
+         status=status.HTTP_400_BAD_REQUEST,
+     )

783-785: 🛠️ Refactor suggestion

Add validation for asset existence and upload status.

The code fetches assets but doesn't validate that all requested asset_ids exist or that the assets are uploaded, which could lead to silent failures.

Add validation after fetching the assets:

original_assets = FileAsset.objects.filter(
    workspace=workspace, id__in=asset_ids
)
+ 
+ # Validate all requested assets exist
+ found_asset_ids = set(str(asset.id) for asset in original_assets)
+ missing_asset_ids = set(asset_ids) - found_asset_ids
+ if missing_asset_ids:
+     return Response(
+         {"error": f"Assets not found: {', '.join(missing_asset_ids)}"},
+         status=status.HTTP_404_NOT_FOUND,
+     )
+ 
+ # Validate all assets are uploaded
+ unuploaded_assets = [str(asset.id) for asset in original_assets if not asset.is_uploaded]
+ if unuploaded_assets:
+     return Response(
+         {"error": f"Assets not uploaded: {', '.join(unuploaded_assets)}"},
+         status=status.HTTP_400_BAD_REQUEST,
+     )

805-806: ⚠️ Potential issue

Add error handling for storage copy operation.

The storage.copy_object() call lacks error handling, which could leave the database in an inconsistent state if the copy fails.

Wrap the storage operation in error handling:

- storage.copy_object(original_asset.asset, destination_key)
- duplicated_assets[str(original_asset.id)] = str(duplicated_asset.id)
+ try:
+     storage.copy_object(original_asset.asset, destination_key)
+     duplicated_assets[str(original_asset.id)] = str(duplicated_asset.id)
+ except Exception as e:
+     # Clean up the created asset record if storage copy fails
+     duplicated_asset.delete()
+     return Response(
+         {"error": f"Failed to copy storage object: {str(e)}"},
+         status=status.HTTP_500_INTERNAL_SERVER_ERROR,
+     )
🧹 Nitpick comments (1)
apiserver/plane/app/views/asset/v2.py (1)

808-814: Consider transaction safety for bulk operations.

The bulk update operation and the preceding asset creation loop could benefit from database transaction wrapping to ensure atomicity.

Consider wrapping the duplication logic in a database transaction:

+ from django.db import transaction
+
+ @transaction.atomic
  def post(self, request, slug):

This ensures that if any part of the duplication process fails, all changes are rolled back consistently.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 0e369f8 and 9704f21.

📒 Files selected for processing (2)
  • apiserver/plane/app/views/asset/v2.py (2 hunks)
  • apiserver/plane/settings/common.py (1 hunks)
🧰 Additional context used
🪛 Ruff (0.11.9)
apiserver/plane/app/views/asset/v2.py

787-787: Line too long (106 > 88)

(E501)

🪛 Pylint (3.3.7)
apiserver/plane/app/views/asset/v2.py

[refactor] 728-728: Too many return statements (7/6)

(R0911)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (3)
apiserver/plane/settings/common.py (1)

74-78: LGTM! Throttling configuration is appropriate.

The throttling settings are well-configured for the new duplicate asset endpoint:

  • AnonRateThrottle provides rate limiting for anonymous users
  • The image_duplicate scope with 10 requests/minute is reasonable for asset duplication operations
  • The anonymous rate of 30 requests/minute is balanced
apiserver/plane/app/views/asset/v2.py (2)

14-14: Import addition looks good.

The ScopedRateThrottle import is necessary for the new duplicate endpoint's throttling functionality.


723-727: Throttling configuration is properly implemented.

The class-level throttling configuration using ScopedRateThrottle with the "image_duplicate" scope addresses the rate limiting requirement mentioned in past reviews.

Comment thread apiserver/plane/app/views/asset/v2.py Outdated
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)
apiserver/plane/app/views/asset/v2.py (4)

727-758: 🛠️ Refactor suggestion

Extract duplicated method to base class.

The get_entity_id_field method is duplicated across multiple endpoint classes in this file (WorkspaceFileAssetEndpoint, ProjectAssetEndpoint, and now DuplicateAssetEndpoint). This violates the DRY principle and creates maintenance overhead.

Consider extracting this method to the BaseAPIView class or creating a mixin class that can be shared across these asset endpoints.

Additionally, the static analysis correctly identifies that this method has too many return statements (7/6), which affects readability. Consider using a dictionary lookup pattern:

def get_entity_id_field(self, entity_type, entity_id):
    entity_field_mapping = {
        FileAsset.EntityTypeContext.WORKSPACE_LOGO: {"workspace_id": entity_id},
        FileAsset.EntityTypeContext.PROJECT_COVER: {"project_id": entity_id},
        FileAsset.EntityTypeContext.USER_AVATAR: {"user_id": entity_id},
        FileAsset.EntityTypeContext.USER_COVER: {"user_id": entity_id},
        FileAsset.EntityTypeContext.ISSUE_ATTACHMENT: {"issue_id": entity_id},
        FileAsset.EntityTypeContext.ISSUE_DESCRIPTION: {"issue_id": entity_id},
        FileAsset.EntityTypeContext.PAGE_DESCRIPTION: {"page_id": entity_id},
        FileAsset.EntityTypeContext.COMMENT_DESCRIPTION: {"comment_id": entity_id},
    }
    return entity_field_mapping.get(entity_type, {})
🧰 Tools
🪛 Pylint (3.3.7)

[refactor] 727-727: Too many return statements (7/6)

(R0911)


764-764: 🛠️ Refactor suggestion

Add validation for entity_type when provided.

The entity_type parameter is accepted but not validated against allowed values. This could lead to invalid entity types being stored in the database.

Add validation similar to other endpoints in this file:

+ if entity_type and entity_type not in FileAsset.EntityTypeContext.values:
+     return Response(
+         {"error": "Invalid entity type.", "status": False},
+         status=status.HTTP_400_BAD_REQUEST,
+     )

775-777: ⚠️ Potential issue

Add validation for asset existence and upload status.

The code fetches the original asset but doesn't validate that the requested asset_id exists or that the asset is uploaded. This could lead to silent failures or attempting to copy non-existent storage objects.

Add validation after fetching the asset:

original_asset = FileAsset.objects.filter(
    workspace=workspace, id=asset_id
).first()
+ 
+ # Validate asset exists
+ if not original_asset:
+     return Response(
+         {"error": "Asset not found"},
+         status=status.HTTP_404_NOT_FOUND,
+     )
+ 
+ # Validate asset is uploaded
+ if not original_asset.is_uploaded:
+     return Response(
+         {"error": "Asset not uploaded"},
+         status=status.HTTP_400_BAD_REQUEST,
+     )

795-795: ⚠️ Potential issue

Add error handling for storage copy operation.

The storage.copy_object() call can fail if the source object doesn't exist or due to storage service issues, but there's no error handling. This could leave the database in an inconsistent state with asset records that don't have corresponding storage objects.

Add error handling around the storage operation:

- storage.copy_object(original_asset.asset, destination_key)
+ try:
+     storage.copy_object(original_asset.asset, destination_key)
+ except Exception as e:
+     # Clean up the created asset record if storage copy fails
+     duplicated_asset.delete()
+     return Response(
+         {"error": f"Failed to copy storage object: {str(e)}"},
+         status=status.HTTP_500_INTERNAL_SERVER_ERROR,
+     )
🧹 Nitpick comments (1)
apiserver/plane/app/views/asset/v2.py (1)

779-779: Fix line length violation.

The line exceeds the maximum length of 88 characters.

- destination_key = f"{workspace.id}/{uuid.uuid4().hex}-{original_asset.attributes.get('name')}"
+ destination_key = (
+     f"{workspace.id}/{uuid.uuid4().hex}-"
+     f"{original_asset.attributes.get('name')}"
+ )
🧰 Tools
🪛 Ruff (0.11.9)

779-779: Line too long (102 > 88)

(E501)

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 9704f21 and cdd6a7b.

📒 Files selected for processing (4)
  • apiserver/plane/app/throttles/asset.py (1 hunks)
  • apiserver/plane/app/urls/asset.py (2 hunks)
  • apiserver/plane/app/views/asset/v2.py (2 hunks)
  • apiserver/plane/settings/common.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apiserver/plane/app/urls/asset.py
  • apiserver/plane/settings/common.py
🧰 Additional context used
🪛 Pylint (3.3.7)
apiserver/plane/app/throttles/asset.py

[refactor] 4-4: Too few public methods (1/2)

(R0903)

apiserver/plane/app/views/asset/v2.py

[refactor] 727-727: Too many return statements (7/6)

(R0911)

🪛 Ruff (0.11.9)
apiserver/plane/app/views/asset/v2.py

779-779: Line too long (102 > 88)

(E501)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (3)
apiserver/plane/app/throttles/asset.py (1)

1-11: LGTM! Clean throttling implementation.

The AssetRateThrottle class correctly implements rate limiting based on asset_id. The implementation follows Django REST framework patterns and properly handles cases where asset_id might not be present.

🧰 Tools
🪛 Pylint (3.3.7)

[refactor] 4-4: Too few public methods (1/2)

(R0903)

apiserver/plane/app/views/asset/v2.py (2)

22-22: LGTM! Proper import of throttling class.

The import correctly adds the AssetRateThrottle class needed for the new endpoint.


723-726: LGTM! Throttling properly configured.

The endpoint correctly applies the AssetRateThrottle to implement rate limiting based on asset ID as requested in previous reviews.

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

♻️ Duplicate comments (2)
apiserver/plane/app/views/asset/v2.py (2)

795-797: storage.copy_object still lacks error handling

A previous review pointed this out. Failure here leaves a DB row without a real object.
Wrap in try/except, roll back the new FileAsset, and return a 500.


727-758: 🛠️ Refactor suggestion

get_entity_id_field is still duplicated – extract or centralise to stay DRY

This helper is now present in three endpoint classes with identical logic. The earlier review already requested moving it into a shared location (e.g., a mix-in or BaseAPIView) and replacing the cascade of if statements with a lookup dict.
Keeping three copies will inevitably drift.

🧰 Tools
🪛 Pylint (3.3.7)

[refactor] 727-727: Too many return statements (7/6)

(R0911)

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between cdd6a7b and 3cb427f.

📒 Files selected for processing (3)
  • apiserver/plane/app/urls/asset.py (2 hunks)
  • apiserver/plane/app/views/__init__.py (1 hunks)
  • apiserver/plane/app/views/asset/v2.py (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apiserver/plane/app/views/init.py
  • apiserver/plane/app/urls/asset.py
🧰 Additional context used
🪛 Ruff (0.11.9)
apiserver/plane/app/views/asset/v2.py

779-779: Line too long (102 > 88)

(E501)

🪛 Pylint (3.3.7)
apiserver/plane/app/views/asset/v2.py

[refactor] 727-727: Too many return statements (7/6)

(R0911)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Analyze (javascript)

Comment thread apiserver/plane/app/views/asset/v2.py Outdated
Comment thread apiserver/plane/app/views/asset/v2.py
Comment thread apiserver/plane/app/views/asset/v2.py Outdated
dheeru0198
dheeru0198 previously approved these changes Nov 13, 2025
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: 7

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 334074d and c0ebe35.

📒 Files selected for processing (8)
  • packages/editor/src/core/extensions/custom-image/components/block.tsx (3 hunks)
  • packages/editor/src/core/extensions/custom-image/components/node-view.tsx (5 hunks)
  • packages/editor/src/core/extensions/custom-image/components/uploader.tsx (8 hunks)
  • packages/editor/src/core/extensions/custom-image/extension.tsx (3 hunks)
  • packages/editor/src/core/extensions/custom-image/utils.ts (3 hunks)
  • packages/editor/src/core/extensions/utility.ts (2 hunks)
  • packages/editor/src/core/props.ts (0 hunks)
  • packages/editor/src/core/types/config.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • packages/editor/src/core/props.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/editor/src/core/extensions/custom-image/utils.ts
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-10-09T20:42:31.843Z
Learnt from: lifeiscontent
Repo: makeplane/plane PR: 7922
File: apps/admin/app/(all)/(dashboard)/ai/form.tsx:19-19
Timestamp: 2025-10-09T20:42:31.843Z
Learning: In the makeplane/plane repository, React types are globally available through TypeScript configuration. Type annotations like React.FC, React.ReactNode, etc. can be used without explicitly importing the React namespace. The codebase uses the modern JSX transform, so React imports are not required for JSX or type references.

Applied to files:

  • packages/editor/src/core/extensions/custom-image/components/uploader.tsx
📚 Learning: 2025-10-01T15:30:17.605Z
Learnt from: lifeiscontent
Repo: makeplane/plane PR: 7888
File: packages/propel/src/avatar/avatar.stories.tsx:2-3
Timestamp: 2025-10-01T15:30:17.605Z
Learning: In the makeplane/plane repository, avoid suggesting inline type imports (e.g., `import { Avatar, type TAvatarSize }`) due to bundler compatibility issues. Keep type imports and value imports as separate statements.

Applied to files:

  • packages/editor/src/core/extensions/custom-image/components/uploader.tsx
🧬 Code graph analysis (5)
packages/editor/src/core/extensions/utility.ts (1)
packages/editor/src/core/plugins/paste-asset.ts (1)
  • PasteAssetPlugin (4-48)
packages/editor/src/core/extensions/custom-image/components/uploader.tsx (1)
packages/editor/src/core/extensions/custom-image/components/node-view.tsx (1)
  • CustomImageNodeViewProps (10-16)
packages/editor/src/core/extensions/custom-image/components/block.tsx (1)
packages/editor/src/core/extensions/custom-image/utils.ts (1)
  • isImageDuplicating (58-58)
packages/editor/src/core/extensions/custom-image/extension.tsx (1)
apps/web/core/services/page/project-page.service.ts (1)
  • duplicate (171-177)
packages/editor/src/core/extensions/custom-image/components/node-view.tsx (1)
packages/editor/src/core/extensions/custom-image/utils.ts (1)
  • hasImageDuplicationFailed (63-64)
🪛 GitHub Actions: Build and lint web apps
packages/editor/src/core/extensions/custom-image/components/node-view.tsx

[error] 5-5: ESLint: Imports 'CustomImageExtensionType' and 'TCustomImageAttributes' are only used as type. (consistent-type-imports)

🪛 GitHub Check: Build and lint web apps
packages/editor/src/core/extensions/custom-image/extension.tsx

[failure] 14-14:
Imports "CustomImageExtensionOptions" and "CustomImageExtensionStorage" are only used as type

packages/editor/src/core/extensions/custom-image/components/node-view.tsx

[failure] 5-5:
Imports "CustomImageExtensionType" and "TCustomImageAttributes" are only used as type

⏰ 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). (3)
  • GitHub Check: Build and lint web apps
  • GitHub Check: Analyze (javascript)
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (11)
packages/editor/src/core/extensions/utility.ts (1)

12-12: LGTM! Clean integration of the paste asset duplication plugin.

The import and registration of PasteAssetPlugin are properly implemented. The plugin is positioned at the end of the ProseMirror plugins array and includes guards (data-uploaded check, hasChanges condition) to prevent conflicts with other paste handlers like MarkdownClipboardPlugin.

Also applies to: 81-81

packages/editor/src/core/extensions/custom-image/extension.tsx (2)

36-46: LGTM!

The duplication option is properly extracted from the file handler and added to the extension options, following the same pattern as the upload functionality.


101-104: LGTM!

Using symbolic attribute names (ECustomImageAttributeNames.ID and ECustomImageAttributeNames.STATUS) improves code maintainability and reduces the risk of typos.

packages/editor/src/core/extensions/custom-image/components/block.tsx (2)

7-7: LGTM!

Import of the duplication helper is appropriate for the new status checks.


206-215: LGTM!

The visibility logic correctly handles the duplication state by showing the loader and hiding interactive controls (toolbar, resizer, upload status) during duplication, which provides a clean user experience.

packages/editor/src/core/extensions/custom-image/components/node-view.tsx (2)

30-31: LGTM!

Using refs to guard retry attempts and prevent concurrent duplication calls is the correct approach, as these don't need to trigger re-renders.


116-135: LGTM!

The rendering logic correctly shows the uploader (with retry UI) when duplication fails and hides the image block, providing a clear path for the user to retry the operation.

packages/editor/src/core/extensions/custom-image/components/uploader.tsx (4)

49-89: LGTM!

Status is correctly updated to UPLOADED on successful upload, and the upload flow properly manages the image lifecycle.


91-97: LGTM!

The upload command now correctly sets the status to UPLOADING before performing the upload, and updateAttributes is properly included in the dependency array.


170-195: LGTM!

Error handling correctly treats duplication failures as error states, and the retry handler properly initiates a new duplication attempt by updating the status to DUPLICATING.


197-223: LGTM!

The UI correctly styles duplication failures with error states, prevents file picker interaction during failures, and uses proper layout classes for consistent rendering.

Comment thread packages/editor/src/core/extensions/custom-image/components/node-view.tsx Outdated
Comment thread packages/editor/src/core/extensions/custom-image/components/uploader.tsx Outdated
Comment thread packages/editor/src/core/extensions/custom-image/components/uploader.tsx Outdated
Comment thread packages/editor/src/core/extensions/custom-image/extension.tsx Outdated
Comment thread packages/editor/src/core/types/config.ts
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

🧹 Nitpick comments (1)
packages/editor/src/core/extensions/custom-image/components/uploader.tsx (1)

169-184: Consider more specific error messages.

The function returns a generic "Error loading image" message for both failedToLoadImage and hasDuplicationFailed cases. Users would benefit from more specific messages to understand whether the issue is with loading the image or duplicating an existing asset.

Consider this refinement:

 const getDisplayMessage = useCallback(() => {
   const isUploading = isImageBeingUploaded;
-  if (failedToLoadImage || hasDuplicationFailed) {
-    return "Error loading image";
+  if (failedToLoadImage) {
+    return "Failed to load image";
+  }
+  if (hasDuplicationFailed) {
+    return "Failed to duplicate asset";
   }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c0ebe35 and af96742.

📒 Files selected for processing (1)
  • packages/editor/src/core/extensions/custom-image/components/uploader.tsx (8 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-10-09T20:42:31.843Z
Learnt from: lifeiscontent
Repo: makeplane/plane PR: 7922
File: apps/admin/app/(all)/(dashboard)/ai/form.tsx:19-19
Timestamp: 2025-10-09T20:42:31.843Z
Learning: In the makeplane/plane repository, React types are globally available through TypeScript configuration. Type annotations like React.FC, React.ReactNode, etc. can be used without explicitly importing the React namespace. The codebase uses the modern JSX transform, so React imports are not required for JSX or type references.

Applied to files:

  • packages/editor/src/core/extensions/custom-image/components/uploader.tsx
📚 Learning: 2025-10-01T15:30:17.605Z
Learnt from: lifeiscontent
Repo: makeplane/plane PR: 7888
File: packages/propel/src/avatar/avatar.stories.tsx:2-3
Timestamp: 2025-10-01T15:30:17.605Z
Learning: In the makeplane/plane repository, avoid suggesting inline type imports (e.g., `import { Avatar, type TAvatarSize }`) due to bundler compatibility issues. Keep type imports and value imports as separate statements.

Applied to files:

  • packages/editor/src/core/extensions/custom-image/components/uploader.tsx
🧬 Code graph analysis (1)
packages/editor/src/core/extensions/custom-image/components/uploader.tsx (1)
packages/editor/src/core/extensions/custom-image/components/node-view.tsx (1)
  • CustomImageNodeViewProps (10-16)
⏰ 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: Analyze (javascript)
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (8)
packages/editor/src/core/extensions/custom-image/components/uploader.tsx (8)

1-1: LGTM: Import additions are correct and necessary.

The RotateCcw icon and ECustomImageStatus enum are properly imported and used throughout the component for the new retry functionality.

Also applies to: 14-14


20-20: LGTM: New prop properly typed and destructured.

The hasDuplicationFailed prop follows the existing pattern and is correctly integrated into the component.

Also applies to: 38-38


54-57: LGTM: Status update on successful upload.

Correctly sets the status to UPLOADED after a successful upload, maintaining proper state transitions.


90-96: LGTM: Upload status properly set before upload operation.

The function correctly sets the status to UPLOADING before initiating the upload, and updateAttributes is properly included in the dependency array. This addresses the past review comment about moving this logic to uploadImageEditorCommand.


198-209: LGTM: Error styling properly applied for duplication failures.

The red error styling is consistently applied across all states (default, hover, selected) when either failedToLoadImage or hasDuplicationFailed is true, providing clear visual feedback to users.


215-219: LGTM: File picker properly disabled during duplication failure.

Correctly prevents opening the file picker when hasDuplicationFailed is true, guiding users to use the retry button instead of uploading a new file.


222-238: LGTM: Retry button implementation is complete and addresses past feedback.

The retry button is properly implemented with:

  • Correct conditional rendering (checks hasDuplicationFailed instead of hardcoded true)
  • type="button" attribute to prevent form submission
  • Proper accessibility with title attribute
  • Consistent styling and clear icon + text affordance
  • Layout enhancement with flex-1 on the message element

This successfully addresses all past review comments regarding the hardcoded condition and missing button type.


186-194: Status change correctly triggers duplication in parent component.

Verification confirms that the parent component (node-view.tsx) contains a useEffect at line 68 that watches for the DUPLICATING status. When handleRetryClick sets the status to DUPLICATING, the effect triggers and invokes the actual duplication logic via handleDuplication(). The dependency array correctly includes status to ensure the effect re-runs when the status changes.

dheeru0198
dheeru0198 previously approved these changes Nov 17, 2025
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

🧹 Nitpick comments (1)
apps/web/core/components/editor/rich-text/description-input/root.tsx (1)

118-118: Duplicate asset flow wiring looks good; consider logging errors for parity

Hooking duplicateEditorAsset from useEditorAsset and exposing it as duplicateFile on RichTextEditor (returning asset_id and surfacing a user-friendly error message) cleanly mirrors the existing upload flow and matches the new backend endpoint shape.

For easier debugging, you might also log the caught error in duplicateFile (similar to the upload path) before throwing the user-facing "Asset duplication failed. Please try again later.", e.g. via console.error.

Also applies to: 225-255

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between af96742 and 548e060.

📒 Files selected for processing (4)
  • apps/web/core/components/editor/document/editor.tsx (2 hunks)
  • apps/web/core/components/editor/lite-text/editor.tsx (2 hunks)
  • apps/web/core/components/editor/rich-text/description-input/root.tsx (2 hunks)
  • apps/web/core/components/editor/rich-text/editor.tsx (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
apps/web/core/components/editor/document/editor.tsx (1)
packages/editor/src/core/types/config.ts (1)
  • TFileHandler (5-22)
apps/web/core/components/editor/rich-text/editor.tsx (1)
packages/editor/src/core/types/config.ts (1)
  • TFileHandler (5-22)
apps/web/core/components/editor/lite-text/editor.tsx (1)
packages/editor/src/core/types/config.ts (1)
  • TFileHandler (5-22)
⏰ 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). (1)
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (3)
apps/web/core/components/editor/document/editor.tsx (1)

28-32: duplicateFile wiring into DocumentEditor looks consistent

duplicateFile is correctly added to the editable props surface and passed through to getEditorFileHandlers with a no-op fallback when editable is false, mirroring the existing uploadFile pattern. This keeps the file handling API consistent across editors and aligns with TFileHandler["duplicate"].

Also applies to: 66-70

apps/web/core/components/editor/rich-text/editor.tsx (1)

28-32: RichTextEditor duplicateFile plumbing is correct and aligned with other editors

The new duplicateFile: TFileHandler["duplicate"] prop for editable: true and its forwarding to getEditorFileHandlers using editable ? props.duplicateFile : async () => "" is consistent with uploadFile and the shared TFileHandler shape.

Also applies to: 64-68

apps/web/core/components/editor/lite-text/editor.tsx (1)

45-48: LiteTextEditor duplicateFile integration matches shared file-handler contract

duplicateFile is properly added to the editable-only props and forwarded into getEditorFileHandlers with a no-op fallback when not editable, matching the established uploadFile pattern and the other editor components.

Also applies to: 122-126

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

♻️ Duplicate comments (1)
packages/editor/src/core/extensions/custom-image/components/node-view.tsx (1)

82-82: Move hasRetriedOnMount flag to prevent blocking automatic retry.

Setting hasRetriedOnMount.current = true before attempting duplication prevents the automatic retry logic (lines 103-109) from ever triggering, even on the first failure. When duplication fails, the status becomes DUPLICATION_FAILED, but the retry effect checks !hasRetriedOnMount.current which evaluates to false, so no retry happens.

Move this line to after successful duplication:

     isDuplicatingRef.current = true;
     try {
-      hasRetriedOnMount.current = true;
-
       const newAssetId = await extension.options.duplicateImage!(imgNodeSrc);

       // Update node with new source and success status
       updateAttributes({
         src: newAssetId,
         status: ECustomImageStatus.UPLOADED,
       });
+      hasRetriedOnMount.current = true;
     } catch (error: unknown) {

This allows the retry effect to run once after initial failure while preventing further retries after success.

🧹 Nitpick comments (1)
packages/editor/src/core/extensions/custom-image/components/node-view.tsx (1)

103-109: Remove misleading comment about delay.

The comment mentions "Add a small delay before retrying" but no delay is actually implemented. The retry happens immediately through the status update.

     if (hasImageDuplicationFailed(status) && !hasRetriedOnMount.current && imgNodeSrc) {
       hasRetriedOnMount.current = true;
-      // Add a small delay before retrying to avoid immediate retries
       updateAttributes({ status: ECustomImageStatus.DUPLICATING });
     }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 548e060 and bb43f2f.

📒 Files selected for processing (2)
  • packages/editor/src/core/extensions/custom-image/components/node-view.tsx (5 hunks)
  • packages/editor/src/core/extensions/custom-image/extension.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/editor/src/core/extensions/custom-image/extension.tsx
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-01T15:30:17.605Z
Learnt from: lifeiscontent
Repo: makeplane/plane PR: 7888
File: packages/propel/src/avatar/avatar.stories.tsx:2-3
Timestamp: 2025-10-01T15:30:17.605Z
Learning: In the makeplane/plane repository, avoid suggesting inline type imports (e.g., `import { Avatar, type TAvatarSize }`) due to bundler compatibility issues. Keep type imports and value imports as separate statements.

Applied to files:

  • packages/editor/src/core/extensions/custom-image/components/node-view.tsx
🧬 Code graph analysis (1)
packages/editor/src/core/extensions/custom-image/components/node-view.tsx (1)
packages/editor/src/core/extensions/custom-image/utils.ts (1)
  • hasImageDuplicationFailed (63-64)
⏰ 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: Build and lint web apps
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (6)
packages/editor/src/core/extensions/custom-image/components/node-view.tsx (6)

5-7: LGTM! Import structure is correct.

The type-only and runtime imports are properly separated, complying with linting rules and bundler compatibility requirements.


20-21: LGTM! Props correctly destructured for duplication logic.

The additions of updateAttributes and status are necessary and correctly integrated with the new duplication flow.


31-32: LGTM! Refs appropriately used for state tracking.

The refs correctly prevent unnecessary re-renders while tracking duplication state and preventing concurrent calls.


111-115: LGTM! Reset logic is correct.

Resetting hasRetriedOnMount when the upload succeeds allows proper retry behavior for future duplication attempts.


117-118: LGTM! Derived values correctly computed.

Both hasDuplicationFailed and shouldShowBlock are properly derived from the current state and used appropriately in rendering logic.


123-136: LGTM! Conditional rendering handles duplication states correctly.

The rendering logic properly switches between image block and uploader based on duplication status, and passes the necessary state to the uploader for appropriate UI feedback.

iam-vipin and others added 2 commits November 20, 2025 13:02
…de-view.tsx

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
iam-vipin
iam-vipin previously approved these changes Nov 20, 2025
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 (2)
apps/space/helpers/editor.helper.ts (1)

61-64: Clarify semantics of duplicate fallback (no-op vs explicit failure)

Right now duplicate resolves with the same assetId, effectively making duplication a silent no-op for sites/space. If upstream code expects a new asset ID or relies on a rejected promise to show the “Asset duplication failed. Please try again later.” message, this may be misleading.

Consider explicitly failing instead, so the UI can surface a clear error when duplication isn’t supported in this app:

-    duplicate: async (assetId: string) =>
-      // Duplication is not supported for sites/space app
-      // Return the same assetId as a fallback
-      assetId,
+    duplicate: async (assetId: string) => {
+      // Duplication is not supported for sites/space app
+      // Explicitly fail so callers can show an appropriate error
+      throw new Error("Asset duplication is not supported for sites/space app");
+    },

If callers are intentionally designed to treat “same ID” as “no-op duplicate” here, please confirm that this is documented and won’t break assumptions elsewhere.

packages/editor/src/core/extensions/custom-image/components/node-view.tsx (1)

68-105: Duplication logic is sound, but consider documenting the retry behavior.

The duplication handler correctly validates inputs, prevents race conditions, and handles errors. However, setting hasRetriedOnMount.current = true at line 82 (before attempting duplication) prevents the automatic retry effect (lines 107-113) from running after failures that occur during this mount.

This appears intentional—automatic retry only triggers for nodes already in DUPLICATION_FAILED state on mount, not for failures during the current session. Consider adding a comment explaining this behavior to prevent confusion.

Example clarifying comment:

       isDuplicatingRef.current = true;
       try {
+        // Set flag before duplication to prevent automatic retry effect after failures in this session.
+        // Automatic retry only applies to nodes that mount in DUPLICATION_FAILED state.
         hasRetriedOnMount.current = true;
 
         const newAssetId = await extension.options.duplicateImage!(imgNodeSrc);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bb43f2f and d420ce7.

📒 Files selected for processing (3)
  • apps/space/helpers/editor.helper.ts (1 hunks)
  • packages/editor/src/ce/helpers/asset-duplication.ts (1 hunks)
  • packages/editor/src/core/extensions/custom-image/components/node-view.tsx (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/editor/src/ce/helpers/asset-duplication.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-01T15:30:17.605Z
Learnt from: lifeiscontent
Repo: makeplane/plane PR: 7888
File: packages/propel/src/avatar/avatar.stories.tsx:2-3
Timestamp: 2025-10-01T15:30:17.605Z
Learning: In the makeplane/plane repository, avoid suggesting inline type imports (e.g., `import { Avatar, type TAvatarSize }`) due to bundler compatibility issues. Keep type imports and value imports as separate statements.

Applied to files:

  • packages/editor/src/core/extensions/custom-image/components/node-view.tsx
🧬 Code graph analysis (1)
packages/editor/src/core/extensions/custom-image/components/node-view.tsx (1)
packages/editor/src/core/extensions/custom-image/utils.ts (1)
  • hasImageDuplicationFailed (63-64)
⏰ 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). (4)
  • GitHub Check: Build and lint web apps
  • GitHub Check: Analyze (javascript)
  • GitHub Check: Build and lint web apps
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (3)
packages/editor/src/core/extensions/custom-image/components/node-view.tsx (3)

5-7: LGTM!

The imports are correctly structured with type-only imports separated from runtime imports, complying with bundler compatibility requirements.


11-32: LGTM!

Props typing and state management are well-structured. The refs appropriately prevent race conditions (via isDuplicatingRef) and track retry state (via hasRetriedOnMount).


115-146: LGTM!

The reset logic correctly clears the retry flag on successful upload, and the rendering logic appropriately shows either the image block or the uploader based on duplication status. The hasDuplicationFailed flag is properly propagated to the uploader component.

@sriramveeraghanta sriramveeraghanta merged commit 8367980 into preview Nov 20, 2025
8 of 11 checks passed
@sriramveeraghanta sriramveeraghanta deleted the chore/asset-duplicate-endpoint branch November 20, 2025 09:35
ClarenceChen0627 pushed a commit to ClarenceChen0627/plane that referenced this pull request Dec 5, 2025
* chore: new asset duplicate endpoint added

* chore: change the type in url

* chore: added rate limiting for image duplication endpoint

* chore: added rate limiting per asset id

* chore: added throttle class

* chore: added validations for entity

* chore: added extra validations

* chore: removed the comment

* chore: reverted the frontend code

* chore: added the response key

* feat: handle image duplication for web

* feat: custom image duplication update

* fix: remove paste logic for image

* fix : remove entity validation

* refactor: remove entity id for duplication

* feat: handle duplication in utils

* feat: add asset duplication registry

* chore: update the set attribute method

* fix: add ref for api check

* chore :remove logs

* chore : add entity types types

* refactor: rename duplication success status value

* chore: update attribute to enums

* chore: update variable name

* chore: set uploading state

* chore : update enum name

* chore : update replace command

* chore: fix retry UI

* chore: remove default logic

* refactor: optimize imports in custom image extension files and improve error handling in image duplication

* fix:type error

* Update packages/editor/src/core/extensions/custom-image/components/node-view.tsx

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* fix: enhance asset duplication handler to ignore HTTP sources

---------

Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
Co-authored-by: Bavisetti Narayan <72156168+NarayanBavisetti@users.noreply.github.com>
Co-authored-by: VipinDevelops <vipinchaudhary1809@gmail.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
ClarenceChen0627 pushed a commit to ClarenceChen0627/plane that referenced this pull request Dec 5, 2025
* chore: new asset duplicate endpoint added

* chore: change the type in url

* chore: added rate limiting for image duplication endpoint

* chore: added rate limiting per asset id

* chore: added throttle class

* chore: added validations for entity

* chore: added extra validations

* chore: removed the comment

* chore: reverted the frontend code

* chore: added the response key

* feat: handle image duplication for web

* feat: custom image duplication update

* fix: remove paste logic for image

* fix : remove entity validation

* refactor: remove entity id for duplication

* feat: handle duplication in utils

* feat: add asset duplication registry

* chore: update the set attribute method

* fix: add ref for api check

* chore :remove logs

* chore : add entity types types

* refactor: rename duplication success status value

* chore: update attribute to enums

* chore: update variable name

* chore: set uploading state

* chore : update enum name

* chore : update replace command

* chore: fix retry UI

* chore: remove default logic

* refactor: optimize imports in custom image extension files and improve error handling in image duplication

* fix:type error

* Update packages/editor/src/core/extensions/custom-image/components/node-view.tsx

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* fix: enhance asset duplication handler to ignore HTTP sources

---------

Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
Co-authored-by: Bavisetti Narayan <72156168+NarayanBavisetti@users.noreply.github.com>
Co-authored-by: VipinDevelops <vipinchaudhary1809@gmail.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
ClarenceChen0627 pushed a commit to ClarenceChen0627/plane that referenced this pull request Dec 5, 2025
* chore: new asset duplicate endpoint added

* chore: change the type in url

* chore: added rate limiting for image duplication endpoint

* chore: added rate limiting per asset id

* chore: added throttle class

* chore: added validations for entity

* chore: added extra validations

* chore: removed the comment

* chore: reverted the frontend code

* chore: added the response key

* feat: handle image duplication for web

* feat: custom image duplication update

* fix: remove paste logic for image

* fix : remove entity validation

* refactor: remove entity id for duplication

* feat: handle duplication in utils

* feat: add asset duplication registry

* chore: update the set attribute method

* fix: add ref for api check

* chore :remove logs

* chore : add entity types types

* refactor: rename duplication success status value

* chore: update attribute to enums

* chore: update variable name

* chore: set uploading state

* chore : update enum name

* chore : update replace command

* chore: fix retry UI

* chore: remove default logic

* refactor: optimize imports in custom image extension files and improve error handling in image duplication

* fix:type error

* Update packages/editor/src/core/extensions/custom-image/components/node-view.tsx

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* fix: enhance asset duplication handler to ignore HTTP sources

---------

Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
Co-authored-by: Bavisetti Narayan <72156168+NarayanBavisetti@users.noreply.github.com>
Co-authored-by: VipinDevelops <vipinchaudhary1809@gmail.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
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.

10 participants