Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement image pasting in md editor #7387

Merged

Conversation

reactoholic
Copy link
Contributor

@reactoholic reactoholic commented Jan 3, 2025

implement image pasting in md editor

Summary by CodeRabbit

  • New Features

    • Enhanced markdown input field with image paste support.
    • Improved character count validation with visual feedback for overflow.
    • Updated event handling and prop management for markdown input components.
    • Integrated user notifications for successful file uploads.
  • Documentation

    • Added clarifying comments for URL protocol handling.

The changes focus on improving the markdown input experience with advanced editing capabilities and better file upload support.

Copy link

coderabbitai bot commented Jan 3, 2025

Walkthrough

The pull request modifies the FormikMarkdownField and MarkdownInput components to enhance their functionality, particularly concerning image handling and user notifications. The handleChange method is renamed to handleOnChange, and various hooks are reordered for clarity. The MarkdownInput component now includes a new handlePaste function to manage pasted images and HTML content. Additionally, a comment is added to the sanitizeUrl function in MarkdownInputControls to clarify its handling of the 'javascript:' protocol.

Changes

File Change Summary
src/core/ui/forms/MarkdownInput/FormikMarkdownField.tsx - Adjusted import order
- Renamed handleChange to handleOnChange
- Updated OutlinedInput to link event handlers to handleOnChange
- Explicitly included controlsVisible, hideImageOptions, and temporaryLocation in inputProps
src/core/ui/forms/MarkdownInput/MarkdownInput.tsx - Added imports for useMemo, useUploadFileMutation, useNotification, and useStorageConfigContext
- Redefined editorOptions using useMemo, including a new handlePaste function
- Set up uploadFile mutation for user notifications
- Updated JSX formatting and conditional rendering in CharacterCountContainer
src/core/ui/forms/MarkdownInputControls/MarkdownInputControls.tsx - Added ESLint comment in sanitizeUrl function to disable no-script-url warning

Possibly related PRs

  • Clean up the use of WrapperButton #7263: The changes in this PR involve the removal of the WrapperButton component and its replacement with the MUI Button component, which aligns with the updates made to the FormikMarkdownField component in the main PR regarding event handling and prop management.

Suggested reviewers

  • bobbykolev

📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ee9af27 and 5c2373c.

📒 Files selected for processing (3)
  • src/core/ui/forms/MarkdownInput/FormikMarkdownField.tsx (4 hunks)
  • src/core/ui/forms/MarkdownInput/MarkdownInput.tsx (4 hunks)
  • src/core/ui/forms/MarkdownInputControls/MarkdownInputControls.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/core/ui/forms/MarkdownInput/FormikMarkdownField.tsx
  • src/core/ui/forms/MarkdownInputControls/MarkdownInputControls.tsx
  • src/core/ui/forms/MarkdownInput/MarkdownInput.tsx

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ 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.
    • Generate unit testing code for this file.
    • 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 generate unit testing code for this file.
    • @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 generate unit testing code.
    • @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.

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. (Beta)
  • @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.

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.

@reactoholic reactoholic linked an issue Jan 3, 2025 that may be closed by this pull request
1 task
Copy link

@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

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b0014c7 and f59cc6f.

📒 Files selected for processing (3)
  • src/core/ui/forms/MarkdownInput/FormikMarkdownField.tsx (8 hunks)
  • src/core/ui/forms/MarkdownInput/MarkdownInput.tsx (4 hunks)
  • src/core/ui/forms/MarkdownInputControls/MarkdownInputControls.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
src/core/ui/forms/MarkdownInputControls/MarkdownInputControls.tsx (1)

Pattern src/**/*.{ts,tsx,js}: Review the React.js/TypeScript/JavaScript code for best practices and potential bugs.
Ensure that the code adheres to TypeScript's typing system and modern standards.
Use the following code guide documentation in the codebase: 'docs/code-guidelines.md'.
Ensure sufficient error handling and logging is present, but note:

  • We have a global error handler in place. So local try/catch blocks are not required unless they provide value beyond what the global error handler already covers.
  • Avoid flagging the absence of local error handling in cases where the global error handler suffices.
  • Avoid flagging the absence of logging in cases where the global logger is already in place (graphql usage).

Inform about the recommendation of not using non-null assertion,
except on GraphQL queries where the variable is asserted non-null and followed by a
skip: with the same variable.
Examples:

  • Not Required: there's a skip
    {
    variables: { templateId: templateId! },
    skip: !templateId,
    }

Check for common security vulnerabilities such as:

  • SQL Injection
  • XSS (Cross-Site Scripting)
  • CSRF (Cross-Site Request Forgery)
  • Insecure dependencies
  • Sensitive data exposure
src/core/ui/forms/MarkdownInput/MarkdownInput.tsx (1)

Pattern src/**/*.{ts,tsx,js}: Review the React.js/TypeScript/JavaScript code for best practices and potential bugs.
Ensure that the code adheres to TypeScript's typing system and modern standards.
Use the following code guide documentation in the codebase: 'docs/code-guidelines.md'.
Ensure sufficient error handling and logging is present, but note:

  • We have a global error handler in place. So local try/catch blocks are not required unless they provide value beyond what the global error handler already covers.
  • Avoid flagging the absence of local error handling in cases where the global error handler suffices.
  • Avoid flagging the absence of logging in cases where the global logger is already in place (graphql usage).

Inform about the recommendation of not using non-null assertion,
except on GraphQL queries where the variable is asserted non-null and followed by a
skip: with the same variable.
Examples:

  • Not Required: there's a skip
    {
    variables: { templateId: templateId! },
    skip: !templateId,
    }

Check for common security vulnerabilities such as:

  • SQL Injection
  • XSS (Cross-Site Scripting)
  • CSRF (Cross-Site Request Forgery)
  • Insecure dependencies
  • Sensitive data exposure
src/core/ui/forms/MarkdownInput/FormikMarkdownField.tsx (1)

Pattern src/**/*.{ts,tsx,js}: Review the React.js/TypeScript/JavaScript code for best practices and potential bugs.
Ensure that the code adheres to TypeScript's typing system and modern standards.
Use the following code guide documentation in the codebase: 'docs/code-guidelines.md'.
Ensure sufficient error handling and logging is present, but note:

  • We have a global error handler in place. So local try/catch blocks are not required unless they provide value beyond what the global error handler already covers.
  • Avoid flagging the absence of local error handling in cases where the global error handler suffices.
  • Avoid flagging the absence of logging in cases where the global logger is already in place (graphql usage).

Inform about the recommendation of not using non-null assertion,
except on GraphQL queries where the variable is asserted non-null and followed by a
skip: with the same variable.
Examples:

  • Not Required: there's a skip
    {
    variables: { templateId: templateId! },
    skip: !templateId,
    }

Check for common security vulnerabilities such as:

  • SQL Injection
  • XSS (Cross-Site Scripting)
  • CSRF (Cross-Site Request Forgery)
  • Insecure dependencies
  • Sensitive data exposure
🔇 Additional comments (28)
src/core/ui/forms/MarkdownInputControls/MarkdownInputControls.tsx (1)

46-46: Good approach allowing protocol checks while disabling the ESLint rule.

By using // eslint-disable-next-line no-script-url, you can explicitly handle dangerous protocols like javascript: for XSS prevention. The accompanying comment clarifies the intent, so this is a well-documented and secure workaround against malicious URLs.

src/core/ui/forms/MarkdownInput/FormikMarkdownField.tsx (22)

1-11: Imports look good.
No issues found with the newly introduced imports or usage.


22-22: Editor import is correct.
Importing Editor to manage the Tiptap editor instance is appropriate.


33-35: New Apollo and notification imports are consistent.
These imports to handle file uploads and notifications align well with the existing architecture.


85-86: Tracking the editor in component state looks good.
Maintaining the editor instance in component state is a solid approach for external manipulations.


87-87: Using useNotification is correct.
The notification hook usage is consistent with the project's pattern.


97-98: Retrieving storage configuration is valid.
This is a suitable way to determine which storage bucket to use for uploads.


101-108: Mutation for file uploads is well integrated.
Reliance on the global error handler is acceptable; no local error handling needed.


109-110: Translation hook usage is appropriate.
Provides localized feedback for users upon file upload success.


111-111: Validation message translation is consistent.
Ensures error messages are localized properly.


Line range hint 129-137: Renamed handleOnChange logic is clear.
Trimming extra <br> tags is a good approach for handling empty content.


203-209: Passing the editor instance upward is useful.
handlePassEditor and inputComponent keep the component flexible.


216-216: Checking labelOffset is a neat conditional.
This ensures the label is positioned correctly based on the editor's layout.


229-229: OutlinedInput usage is appropriate.
No issues with using the MUI component for a multiline Markdown editor.


231-231: Multiline prop is correctly set.
This ensures a suitable text area for Markdown entry.


233-233: Binding field.value is correct.
Keeps the Formik state in sync with the component.


235-235: readOnly prop is handled properly.
The component respects the read-only state as expected.


238-238: Additional props for controlling the component are well integrated.
Allows toggling various behavioral options seamlessly.


243-243: inputComponent usage is a creative approach.
Passing passEditor to the underlying MarkdownInput fosters reusability.


244-244: Applying custom padding matches design guidelines.
Minor styling changes look consistent with the overall UI.


245-245: onBlur handling is straightforward.
Triggers Formik's touched state as expected.


246-247: onPaste and onChange attachments are correct.
Both events are properly integrated to handle input changes and paste events.


249-249: Final closing tag is in place.
No structural issues here.

src/core/ui/forms/MarkdownInput/MarkdownInput.tsx (5)

34-34: passEditor prop is a solid extension point.
Allows the parent component to capture the editor instance for advanced usage.


75-75: Destructuring passEditor is correct.
Retrieves the optional callback properly from props.


100-105: Invoking passEditor inside a useEffect is correct.
Ensures the parent gets the editor instance as soon as it's ready.


286-286: Bounding editor height improves usability.
A maximum height helps to contain large amounts of text in the UI.


289-289: Conditional rendering with CharacterCountContainer is consistent.
Prevents overflow highlighting unless needed.

src/core/ui/forms/MarkdownInput/FormikMarkdownField.tsx Outdated Show resolved Hide resolved
Copy link

@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)
src/core/ui/forms/MarkdownInput/FormikMarkdownField.tsx (2)

101-107: Add error handling for file upload failures.

While the success case is handled well, consider adding error handling to notify users when uploads fail.

 const [uploadFile] = useUploadFileMutation({
   onCompleted: data => {
     notify(t('components.file-upload.file-upload-success'), 'success');
     editor?.commands.setImage({ src: data.uploadFileOnStorageBucket, alt: 'pasted-image' });
   },
+  onError: error => {
+    notify(t('components.file-upload.file-upload-error'), 'error');
+    logError(error);
+  },
 });

196-201: Add cleanup for editor instance.

Consider cleaning up the editor reference when the component unmounts to prevent memory leaks.

+ useEffect(() => {
+   return () => {
+     setEditor(undefined);
+   };
+ }, []);

Also applies to: 236-240

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f59cc6f and 56f893c.

📒 Files selected for processing (1)
  • src/core/ui/forms/MarkdownInput/FormikMarkdownField.tsx (8 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
src/core/ui/forms/MarkdownInput/FormikMarkdownField.tsx (1)

Pattern src/**/*.{ts,tsx,js}: Review the React.js/TypeScript/JavaScript code for best practices and potential bugs.
Ensure that the code adheres to TypeScript's typing system and modern standards.
Use the following code guide documentation in the codebase: 'docs/code-guidelines.md'.
Ensure sufficient error handling and logging is present, but note:

  • We have a global error handler in place. So local try/catch blocks are not required unless they provide value beyond what the global error handler already covers.
  • Avoid flagging the absence of local error handling in cases where the global error handler suffices.
  • Avoid flagging the absence of logging in cases where the global logger is already in place (graphql usage).

Inform about the recommendation of not using non-null assertion,
except on GraphQL queries where the variable is asserted non-null and followed by a
skip: with the same variable.
Examples:

  • Not Required: there's a skip
    {
    variables: { templateId: templateId! },
    skip: !templateId,
    }

Check for common security vulnerabilities such as:

  • SQL Injection
  • XSS (Cross-Site Scripting)
  • CSRF (Cross-Site Request Forgery)
  • Insecure dependencies
  • Sensitive data exposure
🔇 Additional comments (1)
src/core/ui/forms/MarkdownInput/FormikMarkdownField.tsx (1)

1-11: LGTM! Clean implementation of new imports and state management.

The new imports and state management are well-organized and properly typed for handling clipboard events and file uploads.

Also applies to: 33-35, 85-88

src/core/ui/forms/MarkdownInput/FormikMarkdownField.tsx Outdated Show resolved Hide resolved
@reactoholic reactoholic force-pushed the client-web/7385-implement-image-pasting-in-md-editor branch 2 times, most recently from 9f7a004 to c91ddf7 Compare January 3, 2025 15:29
@bobbykolev bobbykolev self-requested a review January 3, 2025 15:56
Copy link
Contributor

@bobbykolev bobbykolev left a comment

Choose a reason for hiding this comment

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

It works very well, I like the feature of pasting multiple images 👏

I left a few comments, nothing critical but double-check the temporary logic.
This passing of the editor looks odd to me, but I can't suggest something better.

Copy link

@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

🔭 Outside diff range comments (1)
src/core/ui/forms/MarkdownInput/FormikMarkdownField.tsx (1)

Line range hint 85-107: Add error handling for file upload failures.

The upload mutation should handle error cases to provide feedback to users when uploads fail.

 const [uploadFile] = useUploadFileMutation({
   onCompleted: data => {
     notify(t('components.file-upload.file-upload-success'), 'success');
     editor?.commands.setImage({ src: data.uploadFileOnStorageBucket, alt: 'pasted-image' });
   },
+  onError: error => {
+    notify(t('components.file-upload.file-upload-error'), 'error');
+    console.error('File upload failed:', error);
+  },
 });
♻️ Duplicate comments (1)
src/core/ui/forms/MarkdownInput/FormikMarkdownField.tsx (1)

138-180: 🛠️ Refactor suggestion

Add file validation checks in paste handler.

The paste handler should validate file types and sizes before uploading:

 const handleOnPaste = useCallback(
   (event: ClipboardEvent) => {
     const clipboardData = event.clipboardData;
     const items = clipboardData.items;

-    if (!items) return;
+    if (!items || !storageConfig?.storageBucketId) return;

-    const storageBucketId = storageConfig?.storageBucketId;
+    const storageBucketId = storageConfig.storageBucketId;
+    const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
+    const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/gif'];

     if (storageBucketId) {
       let hasImage = false;

       for (const item of items) {
-        if (item.type.startsWith('image/')) {
+        if (ALLOWED_TYPES.includes(item.type)) {
           const file = item.getAsFile();
           if (file) {
+            if (file.size > MAX_FILE_SIZE) {
+              notify(t('components.file-upload.file-too-large'), 'error');
+              return;
+            }
             const reader = new FileReader();
🧹 Nitpick comments (1)
src/core/ui/forms/MarkdownInput/FormikMarkdownField.tsx (1)

227-244: Consider maintaining consistent props ordering.

While the functionality is correct, maintaining a consistent props order across components helps with code readability and review efficiency.

Consider following this order:

  1. Core props (multiline, label, value)
  2. Refs and callbacks (inputRef)
  3. State props (readOnly, inputProps)
  4. Event handlers (onBlur, onPaste, onChange)
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 56f893c and dcbd959.

📒 Files selected for processing (3)
  • src/core/ui/forms/MarkdownInput/FormikMarkdownField.tsx (8 hunks)
  • src/core/ui/forms/MarkdownInput/MarkdownInput.tsx (4 hunks)
  • src/core/ui/forms/MarkdownInputControls/MarkdownInputControls.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/core/ui/forms/MarkdownInput/MarkdownInput.tsx
  • src/core/ui/forms/MarkdownInputControls/MarkdownInputControls.tsx
🧰 Additional context used
📓 Path-based instructions (1)
src/core/ui/forms/MarkdownInput/FormikMarkdownField.tsx (1)

Pattern src/**/*.{ts,tsx,js}: Review the React.js/TypeScript/JavaScript code for best practices and potential bugs.
Ensure that the code adheres to TypeScript's typing system and modern standards.
Use the following code guide documentation in the codebase: 'docs/code-guidelines.md'.
Ensure sufficient error handling and logging is present, but note:

  • We have a global error handler in place. So local try/catch blocks are not required unless they provide value beyond what the global error handler already covers.
  • Avoid flagging the absence of local error handling in cases where the global error handler suffices.
  • Avoid flagging the absence of logging in cases where the global logger is already in place (graphql usage).

Inform about the recommendation of not using non-null assertion,
except on GraphQL queries where the variable is asserted non-null and followed by a
skip: with the same variable.
Examples:

  • Not Required: there's a skip
    {
    variables: { templateId: templateId! },
    skip: !templateId,
    }

Check for common security vulnerabilities such as:

  • SQL Injection
  • XSS (Cross-Site Scripting)
  • CSRF (Cross-Site Request Forgery)
  • Insecure dependencies
  • Sensitive data exposure
🔇 Additional comments (2)
src/core/ui/forms/MarkdownInput/FormikMarkdownField.tsx (2)

Line range hint 1-35: Clean and well-organized imports!

The new imports are properly organized and necessary for implementing the image paste functionality.


200-205: Well-implemented editor integration!

The editor instance management is clean and properly memoized using useCallback.

@reactoholic reactoholic force-pushed the client-web/7385-implement-image-pasting-in-md-editor branch from dcbd959 to ce38df9 Compare January 6, 2025 07:44
Copy link

@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)
src/core/ui/forms/MarkdownInput/FormikMarkdownField.tsx (1)

138-180: 🛠️ Refactor suggestion

Enhance paste handler with file validations.

The paste handler should validate file types and sizes before upload.

 const handleOnPaste = useCallback(
   (event: ClipboardEvent) => {
     const clipboardData = event.clipboardData;
     const items = clipboardData.items;

-    if (!items) return;
+    if (!items || !storageConfig?.storageBucketId) return;
+
+    const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
+    const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/gif'];
+    let hasImage = false;

-    const storageBucketId = storageConfig?.storageBucketId;
+    const storageBucketId = storageConfig.storageBucketId;

-    if (storageBucketId) {
-      let hasImage = false;
+    for (const item of items) {
+      if (!ALLOWED_TYPES.includes(item.type)) continue;
+      
+      const file = item.getAsFile();
+      if (!file) continue;
+      
+      if (file.size > MAX_FILE_SIZE) {
+        notify(t('components.file-upload.file-too-large'), 'error');
+        return;
+      }

-      for (const item of items) {
-        if (item.type.startsWith('image/')) {
-          const file = item.getAsFile();
-          if (file) {
-            const reader = new FileReader();
+      const reader = new FileReader();
+      reader.onload = () => {
+        uploadFile({
+          variables: {
+            file,
+            uploadData: {
+              storageBucketId,
+              temporaryLocation: true,
+            },
+          },
+        });
+      };

-            reader.onload = () => {
-              uploadFile({
-                variables: {
-                  file,
-                  uploadData: {
-                    storageBucketId,
-                    temporaryLocation: true,
-                  },
-                },
-              });
-            };

+      reader.onerror = () => {
+        notify(t('components.file-upload.read-error'), 'error');
+      };

       reader.readAsDataURL(file);
       hasImage = true;
-          }
-        }
-      }

       if (hasImage) {
         event.preventDefault();
       }
     }
   },
   [storageConfig?.storageBucketId, uploadFile]
 );
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dcbd959 and ce38df9.

📒 Files selected for processing (3)
  • src/core/ui/forms/MarkdownInput/FormikMarkdownField.tsx (8 hunks)
  • src/core/ui/forms/MarkdownInput/MarkdownInput.tsx (4 hunks)
  • src/core/ui/forms/MarkdownInputControls/MarkdownInputControls.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/core/ui/forms/MarkdownInput/MarkdownInput.tsx
  • src/core/ui/forms/MarkdownInputControls/MarkdownInputControls.tsx
🧰 Additional context used
📓 Path-based instructions (1)
src/core/ui/forms/MarkdownInput/FormikMarkdownField.tsx (1)

Pattern src/**/*.{ts,tsx,js}: Review the React.js/TypeScript/JavaScript code for best practices and potential bugs.
Ensure that the code adheres to TypeScript's typing system and modern standards.
Use the following code guide documentation in the codebase: 'docs/code-guidelines.md'.
Ensure sufficient error handling and logging is present, but note:

  • We have a global error handler in place. So local try/catch blocks are not required unless they provide value beyond what the global error handler already covers.
  • Avoid flagging the absence of local error handling in cases where the global error handler suffices.
  • Avoid flagging the absence of logging in cases where the global logger is already in place (graphql usage).

Inform about the recommendation of not using non-null assertion,
except on GraphQL queries where the variable is asserted non-null and followed by a
skip: with the same variable.
Examples:

  • Not Required: there's a skip
    {
    variables: { templateId: templateId! },
    skip: !templateId,
    }

Check for common security vulnerabilities such as:

  • SQL Injection
  • XSS (Cross-Site Scripting)
  • CSRF (Cross-Site Request Forgery)
  • Insecure dependencies
  • Sensitive data exposure
🔇 Additional comments (1)
src/core/ui/forms/MarkdownInput/FormikMarkdownField.tsx (1)

202-205: LGTM! Good use of memoization and event handling.

The component properly memoizes the input component and correctly wires up the event handlers.

Also applies to: 240-244

src/core/ui/forms/MarkdownInput/FormikMarkdownField.tsx Outdated Show resolved Hide resolved
Copy link

@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

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ce38df9 and bfe09fd.

📒 Files selected for processing (2)
  • src/core/ui/forms/MarkdownInput/FormikMarkdownField.tsx (8 hunks)
  • src/core/ui/forms/MarkdownInput/MarkdownInput.tsx (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/core/ui/forms/MarkdownInput/MarkdownInput.tsx
🧰 Additional context used
📓 Path-based instructions (1)
src/core/ui/forms/MarkdownInput/FormikMarkdownField.tsx (1)

Pattern src/**/*.{ts,tsx,js}: Review the React.js/TypeScript/JavaScript code for best practices and potential bugs.
Ensure that the code adheres to TypeScript's typing system and modern standards.
Use the following code guide documentation in the codebase: 'docs/code-guidelines.md'.
Ensure sufficient error handling and logging is present, but note:

  • We have a global error handler in place. So local try/catch blocks are not required unless they provide value beyond what the global error handler already covers.
  • Avoid flagging the absence of local error handling in cases where the global error handler suffices.
  • Avoid flagging the absence of logging in cases where the global logger is already in place (graphql usage).

Inform about the recommendation of not using non-null assertion,
except on GraphQL queries where the variable is asserted non-null and followed by a
skip: with the same variable.
Examples:

  • Not Required: there's a skip
    {
    variables: { templateId: templateId! },
    skip: !templateId,
    }

Check for common security vulnerabilities such as:

  • SQL Injection
  • XSS (Cross-Site Scripting)
  • CSRF (Cross-Site Request Forgery)
  • Insecure dependencies
  • Sensitive data exposure
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Travis CI - Pull Request
🔇 Additional comments (2)
src/core/ui/forms/MarkdownInput/FormikMarkdownField.tsx (2)

164-166: Verify temporary storage usage policy.

The temporaryLocation flag is always set to the prop value, but there's uncertainty about when to use temporary storage. This needs clarification from the backend team.

✅ Verification successful

Temporary storage usage policy is well-documented

The usage pattern is clearly defined in useStorageConfig.tsx: temporaryLocation should be false by default and set to true only for specific components that need it (e.g., callout creation).

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other usages of temporaryLocation to understand the pattern
rg -A 5 "temporaryLocation.*true" 

Length of output: 1236


141-183: 🛠️ Refactor suggestion

Enhance image paste validation.

The paste handler should include file size and type validations to prevent issues with large or unsupported files.

 const handleOnPaste = useCallback(
   (event: ClipboardEvent) => {
     const clipboardData = event.clipboardData;
     const items = clipboardData.items;

-    if (!items) return;
+    if (!items || !storageConfig?.storageBucketId) return;

-    const storageBucketId = storageConfig?.storageBucketId;
+    const storageBucketId = storageConfig.storageBucketId;
+    const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
+    const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/gif'];

-    if (storageBucketId) {
-      let hasImage = false;
+    let hasImage = false;

-      for (const item of items) {
-        if (item.type.startsWith('image/')) {
-          const file = item.getAsFile();
-          if (file) {
+    for (const item of items) {
+      if (ALLOWED_TYPES.includes(item.type)) {
+        const file = item.getAsFile();
+        if (file) {
+          if (file.size > MAX_FILE_SIZE) {
+            notify(t('components.file-upload.file-too-large'), 'error');
+            return;
+          }

           const reader = new FileReader();
           reader.onload = () => {
             uploadFile({
               variables: {
                 file,
                 uploadData: {
                   storageBucketId,
                   temporaryLocation,
                 },
               },
             });
           };

+          reader.onerror = () => {
+            notify(t('components.file-upload.read-error'), 'error');
+          };

           reader.readAsDataURL(file);
           hasImage = true;
         }
       }
     }

     if (hasImage) {
       event.preventDefault();
     }
-    }
   },
   [storageConfig?.storageBucketId, uploadFile, notify]
 );

Likely invalid or redundant comment.

src/core/ui/forms/MarkdownInput/FormikMarkdownField.tsx Outdated Show resolved Hide resolved
@reactoholic reactoholic force-pushed the client-web/7385-implement-image-pasting-in-md-editor branch from e9a9d11 to ee9af27 Compare January 6, 2025 14:09
Copy link

@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)
src/core/ui/forms/MarkdownInput/FormikMarkdownField.tsx (1)

217-222: Remove unused dependency from useCallback.

The handleOnPaste dependency is not used in the callback and should be removed.

 const inputComponent = useCallback(
   (props: PropsWithChildren<InputBaseComponentProps>) => (
     <MarkdownInput {...props} pasteImageHandler={handleImagePaste} />
   ),
-  [handleImagePaste, handleOnPaste]
+  [handleImagePaste]
 );
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bfe09fd and ee9af27.

📒 Files selected for processing (2)
  • src/core/ui/forms/MarkdownInput/FormikMarkdownField.tsx (8 hunks)
  • src/core/ui/forms/MarkdownInput/MarkdownInput.tsx (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/core/ui/forms/MarkdownInput/MarkdownInput.tsx
🧰 Additional context used
📓 Path-based instructions (1)
src/core/ui/forms/MarkdownInput/FormikMarkdownField.tsx (1)

Pattern src/**/*.{ts,tsx,js}: Review the React.js/TypeScript/JavaScript code for best practices and potential bugs.
Ensure that the code adheres to TypeScript's typing system and modern standards.
Use the following code guide documentation in the codebase: 'docs/code-guidelines.md'.
Ensure sufficient error handling and logging is present, but note:

  • We have a global error handler in place. So local try/catch blocks are not required unless they provide value beyond what the global error handler already covers.
  • Avoid flagging the absence of local error handling in cases where the global error handler suffices.
  • Avoid flagging the absence of logging in cases where the global logger is already in place (graphql usage).

Inform about the recommendation of not using non-null assertion,
except on GraphQL queries where the variable is asserted non-null and followed by a
skip: with the same variable.
Examples:

  • Not Required: there's a skip
    {
    variables: { templateId: templateId! },
    skip: !templateId,
    }

Check for common security vulnerabilities such as:

  • SQL Injection
  • XSS (Cross-Site Scripting)
  • CSRF (Cross-Site Request Forgery)
  • Insecure dependencies
  • Sensitive data exposure
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Travis CI - Pull Request
🔇 Additional comments (3)
src/core/ui/forms/MarkdownInput/FormikMarkdownField.tsx (3)

Line range hint 1-277: Overall implementation looks good!

The image paste functionality is well-implemented with proper separation of concerns. The suggested improvements will enhance the robustness and user experience of the feature.


141-195: 🛠️ Refactor suggestion

Enhance paste handler with validation and loading state.

The paste handler should include file type/size validation and show loading state during upload.

+ const ALLOWED_IMAGE_TYPES = ['image/jpeg', 'image/png', 'image/gif'];
+ const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB

 const handleOnPaste = useCallback(
   (event: ClipboardEvent) => {
     const clipboardData = event.clipboardData;
     const items = clipboardData?.items;

     if (!items) return;

     const storageBucketId = storageConfig?.storageBucketId;

     if (storageBucketId) {
       let imageProcessed = false;

       for (const item of items) {
         if (item.kind === 'file' && item.type.startsWith('image/')) {
           const file = item.getAsFile();
           if (file) {
+            if (!ALLOWED_IMAGE_TYPES.includes(file.type)) {
+              notify(t('components.file-upload.invalid-type'), 'error');
+              return;
+            }
+            if (file.size > MAX_FILE_SIZE) {
+              notify(t('components.file-upload.file-too-large'), 'error');
+              return;
+            }
+
             const reader = new FileReader();

             reader.onload = () => {
+              editor?.setOptions({ editable: false });
               uploadFile({
                 variables: {
                   file,
                   uploadData: {
                     storageBucketId,
                     temporaryLocation,
                   },
                 },
+              }).finally(() => {
+                editor?.setOptions({ editable: true });
+              });
             };

             reader.readAsDataURL(file);
             imageProcessed = true;
             break;
           }
         }

Likely invalid or redundant comment.


101-110: 🛠️ Refactor suggestion

Improve error handling in upload mutation.

The error handling should use the notification system for consistency and provide a better user experience.

 const [uploadFile] = useUploadFileMutation({
   onCompleted: data => {
     notify(t('components.file-upload.file-upload-success'), 'success');
     editor?.commands.setImage({ src: data.uploadFileOnStorageBucket, alt: 'pasted-image' });
   },
   onError: error => {
-    console.error(error.message);
+    notify(t('components.file-upload.file-upload-error'), 'error');
   },
 });

Likely invalid or redundant comment.

@reactoholic reactoholic force-pushed the client-web/7385-implement-image-pasting-in-md-editor branch from ee9af27 to b6c9c52 Compare January 6, 2025 14:16
@reactoholic reactoholic force-pushed the client-web/7385-implement-image-pasting-in-md-editor branch from b6c9c52 to 9a26fc7 Compare January 6, 2025 14:19
bobbykolev
bobbykolev previously approved these changes Jan 8, 2025
Copy link
Contributor

@bobbykolev bobbykolev left a comment

Choose a reason for hiding this comment

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

Let's remove the redundant changes.
Otherwise, the pasting is handled and works very well 👏

One small thing that could be extracted as an enhancement is a loading indication. If you paste a large file under a slow network, nothing happens for seconds.

@bobbykolev bobbykolev merged commit 4f6da1a into develop Jan 8, 2025
3 checks passed
@bobbykolev bobbykolev deleted the client-web/7385-implement-image-pasting-in-md-editor branch January 8, 2025 12:44
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.

Implement pasting of image content in markdown editor
2 participants