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

[WEB-3698] fix: comments refactor #6759

Merged
merged 13 commits into from
Mar 27, 2025
Merged

Conversation

gakshita
Copy link
Collaborator

@gakshita gakshita commented Mar 17, 2025

Description

  • Comments now display an “edited” indicator when modified.
  • Introduced a new CommentsWrapper component, that is used globally for comments to achieve consistent UI
  • Added a hover state for comments to display complete timestamp of the comments.
  • Added new translation entries for "edited" and "bot" in multiple languages to enhance localization support.
  • Comment operations have been consolidated in a dedicated hook useCommentOperations in all components implementing comments

References

[WEB-3698]

@gakshita gakshita marked this pull request as draft March 17, 2025 08:29
Copy link
Contributor

coderabbitai bot commented Mar 17, 2025

Walkthrough

The changes modify how issue comments are updated and managed across both the backend API and frontend components. The API now conditionally updates the comment’s edit timestamp, while the TypeScript definitions have been extended to support reactions and various comment operations. The frontend has seen the introduction of new and renamed components for displaying, creating, and reacting to comments, along with updates to editor components and services. Additionally, helper hooks and store updates streamline comment operations and metadata tracking throughout the system.

Changes

File(s) Change Summary
apiserver/plane/app/views/issue/comment.py Modified partial_update in IssueCommentViewSet to conditionally add edited_at timestamp based on comment content.
packages/types/src/issues/activity/issue_comment.d.ts Added new types (TCommentReaction, TCommentsOperations) and updated TIssueComment with an optional edited_at field.
web/ce/components/comments/comment-block.tsx
web/ce/components/comments/index.ts
Introduced new CommentBlock component and updated export to help display comments with user details and quick actions.
web/core/components/comments/comment-card.tsx
web/core/components/comments/comment-create.tsx
web/core/components/comments/comment-reaction.tsx
web/core/components/comments/comments.tsx
web/core/components/comments/index.ts (deleted files: comment-block.tsx, index.ts, root.tsx)
Renamed and refactored comment components (e.g., IssueCommentCardCommentCard, IssueCommentCreateCommentCreate), introduced CommentReactions and CommentsWrapper, and removed deprecated modules.
web/core/components/issues/issue-detail/issue-activity/activity-comment-root.tsx
web/core/components/issues/issue-detail/issue-activity/root.tsx
Updated comment handling in issue activity components to use the new comment components and operations; swapped out old functions and hooks for a new consolidated hook.
web/core/components/editor/lite-text-editor/lite-text-editor.tsx
web/core/components/editor/lite-text-editor/lite-text-read-only-editor.tsx
Made projectId optional and added new optional props (showToolbar, parentClassName) to enhance editor flexibility.
web/core/services/file.service.ts
web/core/services/issue/issue_comment.service.ts
Added updateBulkWorkspaceAssetsUploadStatus to handle asset uploads at the workspace level and updated patchIssueComment to return the updated comment object.
web/core/store/issue/issue-details/comment.store.ts
web/core/store/issue/issue-details/comment_reaction.store.ts
Updated the comment store to maintain and update updated_at/edited_at timestamps and ensured proper initialization and access of comment reaction data.
web/core/components/issues/issue-detail/issue-activity/helper.tsx Added new hook useCommentOperations to consolidate comment-related CRUD and reaction functions.
web/ee/components/comments/index.ts Added export to re-export all entities from ce/components/comments for consolidated access.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant ViewSet
    participant Serializer
    participant ActivityHandler

    Client->>ViewSet: PATCH /issue/{id} with comment_html
    ViewSet->>ViewSet: Check if comment_html exists and has changed
    alt Edited Content Changed
       ViewSet->>Serializer: save(edited_at = now)
    else No Change Detected
       ViewSet->>Serializer: save()
    end
    Serializer-->>ViewSet: return updated comment
    ViewSet->>ActivityHandler: trigger activity delay
    ActivityHandler-->>ViewSet: activity processed
    ViewSet-->>Client: return response
Loading

Possibly related PRs

Suggested labels

🌟enhancement, 🧹chore, 🌐frontend

Suggested reviewers

  • sriramveeraghanta
  • SatishGandham
  • rahulramesha

Poem

I’m a little rabbit, hopping with cheer,
Code changes abound, making updates clear.
Comments updated with a timely twist,
New features and hooks that none can resist.
In fields of refactor, I dance away –
💻🐰 Hip-hip-hooray for a brighter day!
Hop on, code—let’s live to debug and play!


📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 4d76908 and fd0dbf7.

📒 Files selected for processing (1)
  • packages/i18n/src/locales/en/translations.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/i18n/src/locales/en/translations.json
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Analyze (javascript)

🪧 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.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai plan to trigger planning for file edits and PR creation.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

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

CodeRabbit Configuration File (.coderabbit.yaml)

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

Documentation and Community

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

@gakshita gakshita marked this pull request as ready for review March 25, 2025 08:10
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🔭 Outside diff range comments (1)
web/core/store/issue/issue-details/comment_reaction.store.ts (1)

182-182: ⚠️ Potential issue

Potential bug in reaction removal.

The removal code is deleting this.commentReactionMap[reaction] but should likely be deleting this.commentReactionMap[currentReaction.id]. This could be removing entire reaction categories rather than specific reaction instances.

- delete this.commentReactionMap[reaction];
+ delete this.commentReactionMap[currentReaction.id];
🧹 Nitpick comments (11)
packages/types/src/issues/activity/issue_comment.d.ts (3)

28-28: Redundant type definition

The edited_at property has both the optional property notation (?) and the union with undefined, which is redundant since the optional property notation already implies that the property could be undefined.

- edited_at?: string | undefined;
+ edited_at?: string;

33-33: Consider using a more specific type

The comment_reactions property is typed as any[], which doesn't provide type safety. Consider using the newly defined TCommentReaction[] instead for better type checking.

- comment_reactions: any[];
+ comment_reactions: TCommentReaction[];

42-57: Well-defined operations interface with some inconsistencies

The TCommentsOperations interface provides a comprehensive set of operations for comment management. However, there's an inconsistency in parameter types: deleteCommentReaction accepts userReactions: TCommentReaction[] while react accepts userReactions: string[].

Consider making these parameter types consistent based on your intended usage pattern.

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

109-135: Consider simplifying toolbar visibility logic.

While the conditional rendering based on showToolbar is good, the component still uses both showToolbar and isFocused states to control toolbar visibility. This dual mechanism could be confusing.

You might want to simplify the logic:

-      {showToolbar && (
-        <div
-          className={cn(
-            "transition-all duration-300 ease-out origin-top overflow-hidden",
-            isFocused ? "max-h-[200px] opacity-100 scale-y-100 mt-3" : "max-h-0 opacity-0 scale-y-0 invisible"
-          )}
-        >
+      {showToolbar && (
+        <div
+          className={cn(
+            "transition-all duration-300 ease-out origin-top overflow-hidden",
+            isFocused ? "max-h-[200px] opacity-100 scale-y-100 mt-3" : "max-h-0 opacity-0 scale-y-0 invisible"
+          )}
+        >

Consider adding a more explicit comment explaining how showToolbar (render/don't render) and isFocused (animate visibility) work together.

web/core/store/issue/issue-details/comment_reaction.store.ts (1)

147-147: Consider addressing the ESLint warning properly.

Adding a disable comment for no-useless-catch suggests there's an underlying issue with the error handling pattern. Consider either removing the try-catch block if it's not adding value, or implementing proper error handling.

-  // eslint-disable-next-line no-useless-catch
   try {
     // existing code
   } catch (error) {
-    throw error;
+    // Add proper error handling here
+    console.error("Failed to create reaction:", error);
+    throw error;
   }
web/core/components/comments/comments.tsx (1)

38-59: Consider handling empty state and improving type checking.

The component handles both string IDs and direct comment objects, but could benefit from:

  1. An empty state UI when there are no comments
  2. More explicit handling of the mixed types in the comments array
  3. A fallback UI for when getCommentById returns undefined
  <div className="flex-grow py-4 overflow-y-auto">
+   {comments?.length === 0 && (
+     <div className="flex justify-center items-center h-full text-custom-text-300 text-sm">
+       No comments yet
+     </div>
+   )}
    {comments?.map((r, index) => {
      let comment;
      if (typeof r === "string") {
        comment = getCommentById?.(r);
      } else {
        comment = r;
      }

-     if (!comment) return null;
+     if (!comment) return <div className="text-custom-text-300 text-sm">Comment not found</div>;
      return (
        <CommentCard
          key={comment.id}
          workspaceSlug={workspaceSlug}
          comment={comment as TIssueComment}
          activityOperations={activityOperations}
          disabled={!isEditingAllowed}
          ends={index === 0 ? "top" : index === comments.length - 1 ? "bottom" : undefined}
          projectId={projectId}
        />
      );
    })}
  </div>
web/core/components/comments/comment-reaction.tsx (1)

38-64: Fix unnecessary fragment and use optional chaining.

There are a couple of small issues in the reaction rendering code:

  1. The fragment on lines 42-62 is unnecessary since it wraps a single child element.
  2. Missing optional chaining when accessing reactionIds length.
  {reactionIds &&
    Object.keys(reactionIds || {}).map(
      (reaction: string) =>
        reactionIds[reaction]?.length > 0 && (
-         <>
          <Tooltip tooltipContent={activityOperations.getReactionUsers(reaction, reactionIds)}>
            <button
              type="button"
              onClick={() => !disabled && activityOperations.react(comment.id, reaction, userReactions)}
              key={reaction}
              className={cn(
                "flex h-full items-center gap-1 rounded-md px-2 py-1 text-sm text-custom-text-100",
                userReactions.includes(reaction) ? "bg-custom-primary-100/10" : "bg-custom-background-80",
                {
                  "cursor-not-allowed": disabled,
                }
              )}
            >
              <span>{renderEmoji(reaction)}</span>
              <span className={userReactions.includes(reaction) ? "text-custom-primary-100" : ""}>
-               {(reactionIds || {})[reaction].length}{" "}
+               {(reactionIds || {})[reaction]?.length}{" "}
              </span>
            </button>
          </Tooltip>
-         </>
        )
    )}
🧰 Tools
🪛 Biome (1.9.4)

[error] 58-58: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 42-62: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)

web/core/components/issues/issue-detail/issue-activity/activity-comment-root.tsx (1)

53-62: Add null check for getCommentById result.

The component directly uses the result of getCommentById without checking if it returns a valid comment object. This could lead to runtime errors if the comment doesn't exist.

  {filteredActivityComments.map((activityComment, index) =>
    activityComment.activity_type === "COMMENT" ? (
+     (() => {
+       const comment = getCommentById(activityComment.id);
+       return comment ? (
        <CommentCard
          key={activityComment.id}
          workspaceSlug={workspaceSlug}
-         comment={getCommentById(activityComment.id)}
+         comment={comment}
          activityOperations={activityOperations}
          ends={index === 0 ? "top" : index === filteredActivityComments.length - 1 ? "bottom" : undefined}
          showAccessSpecifier={showAccessSpecifier}
          disabled={disabled}
          projectId={projectId}
        />
+       ) : null;
+     })()
    ) : activityComment.activity_type === "ACTIVITY" ? (
web/core/components/comments/comment-create.tsx (1)

28-29: Consider adding error handling around fileService operations
Currently, any failure within FileService calls is neither caught here nor signaled upstream. You might want to handle or propagate errors to provide more robust feedback.

 const fileService = new FileService();

 export const CommentCreate: FC<TCommentCreate> = observer((props) => {
+  // Consider try/catch around fileService call sites for reliability
web/core/components/comments/comment-card.tsx (1)

125-131: Optional confirmation before removing comments
Currently, the delete action is triggered immediately. Consider adding a confirmation dialog or an undo flow to avoid accidental deletion.

web/core/components/issues/issue-detail/issue-activity/helper.tsx (1)

1-159: Overall design of useCommentOperations is solid
The dedicated hook centralizes comment creation, updates, and reactions gracefully. One minor note: operations like createComment return undefined on error, so calling code won’t easily detect a failure. Consider re-throwing or returning a rejected promise to ensure upstream handlers can react to errors.

+    } catch (error) {
+      setToast({...});
+      throw error; // re-throw to allow callers to handle the error too
+    }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 3f22642 and b4cd1e0.

📒 Files selected for processing (24)
  • apiserver/plane/app/views/issue/comment.py (1 hunks)
  • packages/types/src/issues/activity/issue_comment.d.ts (3 hunks)
  • web/ce/components/comments/comment-block.tsx (1 hunks)
  • web/ce/components/comments/index.ts (1 hunks)
  • web/core/components/comments/comment-card.tsx (6 hunks)
  • web/core/components/comments/comment-create.tsx (5 hunks)
  • web/core/components/comments/comment-reaction.tsx (1 hunks)
  • web/core/components/comments/comments.tsx (1 hunks)
  • web/core/components/comments/index.ts (1 hunks)
  • web/core/components/editor/lite-text-editor/lite-text-editor.tsx (4 hunks)
  • web/core/components/editor/lite-text-editor/lite-text-read-only-editor.tsx (1 hunks)
  • web/core/components/issues/issue-detail/issue-activity/activity-comment-root.tsx (3 hunks)
  • web/core/components/issues/issue-detail/issue-activity/activity/actions/helpers/activity-block.tsx (1 hunks)
  • web/core/components/issues/issue-detail/issue-activity/comments/comment-block.tsx (0 hunks)
  • web/core/components/issues/issue-detail/issue-activity/comments/index.ts (0 hunks)
  • web/core/components/issues/issue-detail/issue-activity/comments/root.tsx (0 hunks)
  • web/core/components/issues/issue-detail/issue-activity/helper.tsx (1 hunks)
  • web/core/components/issues/issue-detail/issue-activity/index.ts (0 hunks)
  • web/core/components/issues/issue-detail/issue-activity/root.tsx (5 hunks)
  • web/core/services/file.service.ts (1 hunks)
  • web/core/services/issue/issue_comment.service.ts (1 hunks)
  • web/core/store/issue/issue-details/comment.store.ts (1 hunks)
  • web/core/store/issue/issue-details/comment_reaction.store.ts (1 hunks)
  • web/ee/components/comments/index.ts (1 hunks)
💤 Files with no reviewable changes (4)
  • web/core/components/issues/issue-detail/issue-activity/index.ts
  • web/core/components/issues/issue-detail/issue-activity/comments/root.tsx
  • web/core/components/issues/issue-detail/issue-activity/comments/index.ts
  • web/core/components/issues/issue-detail/issue-activity/comments/comment-block.tsx
🧰 Additional context used
🧬 Code Definitions (8)
web/ce/components/comments/comment-block.tsx (1)
packages/types/src/issues/activity/issue_comment.d.ts (1)
  • TIssueComment (17-40)
web/core/components/issues/issue-detail/issue-activity/activity-comment-root.tsx (2)
packages/types/src/issues/activity/issue_comment.d.ts (1)
  • TCommentsOperations (42-57)
web/core/components/comments/comment-card.tsx (1)
  • CommentCard (33-214)
web/core/components/issues/issue-detail/issue-activity/helper.tsx (1)
packages/types/src/issues/activity/issue_comment.d.ts (2)
  • TCommentsOperations (42-57)
  • TIssueComment (17-40)
web/core/components/comments/comments.tsx (3)
packages/types/src/issues/activity/issue_comment.d.ts (2)
  • TCommentsOperations (42-57)
  • TIssueComment (17-40)
web/core/components/comments/comment-create.tsx (1)
  • CommentCreate (30-128)
web/core/components/comments/comment-card.tsx (1)
  • CommentCard (33-214)
web/core/components/comments/comment-reaction.tsx (1)
packages/types/src/issues/activity/issue_comment.d.ts (2)
  • TIssueComment (17-40)
  • TCommentsOperations (42-57)
packages/types/src/issues/activity/issue_comment.d.ts (1)
packages/types/src/users.d.ts (1)
  • IUserLite (6-14)
web/core/components/editor/lite-text-editor/lite-text-editor.tsx (1)
packages/editor/src/core/types/config.ts (1)
  • TFileHandler (8-20)
web/core/components/issues/issue-detail/issue-activity/root.tsx (2)
web/core/components/issues/issue-detail/issue-activity/helper.tsx (1)
  • useCommentOperations (9-158)
web/core/components/comments/comment-create.tsx (1)
  • CommentCreate (30-128)
🪛 Biome (1.9.4)
web/core/components/comments/comment-reaction.tsx

[error] 58-58: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 42-62: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)

🔇 Additional comments (41)
packages/types/src/issues/activity/issue_comment.d.ts (1)

11-16: Well-structured type for comment reactions

The TCommentReaction type is well-designed, providing a clear structure for comment reactions with appropriate properties including the user details.

web/ce/components/comments/index.ts (1)

1-1: Clean barrel export

This is a good pattern for creating a centralized entry point for the comment block components, making imports cleaner throughout the codebase.

web/core/components/comments/index.ts (1)

1-1: Clean barrel export

This re-export pattern provides a centralized access point for importing all comment-related components, which aligns well with the refactoring goals mentioned in the PR.

web/ee/components/comments/index.ts (1)

1-1: Good code reuse across editions

This export statement enables code reuse between the Community Edition (CE) and Enterprise Edition (EE) components, which is a good practice for maintaining consistency while allowing for edition-specific extensions.

web/core/components/issues/issue-detail/issue-activity/activity/actions/helpers/activity-block.tsx (1)

51-51: Enhanced time display styling

The addition of text-custom-text-350 class to the time display provides better visual hierarchy, making the timestamp appear in a lighter shade to differentiate it from the main content.

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

20-20: Good enhancement to component flexibility

Making projectId optional allows this component to be used in more contexts where project information might not be available or necessary.

web/core/store/issue/issue-details/comment.store.ts (1)

158-161: Improved timestamp handling

The added code properly updates the comment's updated_at and edited_at timestamps from the API response, ensuring the UI displays accurate timing information for edited comments. The use of runInAction correctly follows MobX best practices for handling state updates.

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

112-124: Good addition of bulk workspace assets update functionality

This new method complements the existing project-level method by providing similar functionality at the workspace level, supporting operations where multiple file assets need to be processed together.

The implementation follows the same pattern as other similar methods in this service, maintaining consistency in the codebase.

web/core/services/issue/issue_comment.service.ts (1)

66-66: Good return type update for better type safety.

Changing the return type from Promise<void> to Promise<TIssueComment> correctly aligns with the implementation, which already returns the response data. This type correction enables consumers of this method to access the updated comment data, including fields like edited_at or updated_at.

apiserver/plane/app/views/issue/comment.py (1)

108-114: Good implementation of edited_at tracking.

This change intelligently updates the edited_at timestamp only when the actual comment content changes, not for other types of updates. This approach matches user expectations since users typically expect the "edited" indicator to appear only when content has been modified.

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

26-26: Making projectId optional improves component flexibility.

Making projectId optional allows the component to be used in contexts where a project may not be relevant or available, enhancing the component's reusability.


33-37: Good addition of customization options.

Adding showToolbar and parentClassName props increases the component's flexibility, allowing for different UI configurations and custom styling depending on where it's used.


53-54: Sensible default values for new props.

Setting sensible defaults (showToolbar = true and parentClassName = "") ensures backward compatibility while enabling the new customization options.


84-84: Good use of utility function for className composition.

Using the cn utility function for combining classNames with the new parentClassName prop is a clean and maintainable approach to styling composition.

web/core/store/issue/issue-details/comment_reaction.store.ts (2)

153-153: Good defensive programming.

Adding a null check before accessing the reactions object prevents potential runtime errors when working with a new comment that doesn't have any reactions yet.


155-155: Improved object path handling.

Changing from array notation to template string syntax for updating nested objects is more readable and consistent with the lodash update function's expectations.

web/core/components/comments/comments.tsx (3)

12-19: Props type defined clearly with good TypeScript typing.

The TCommentsWrapper type is well-structured and provides clear typing for the component's props. The optional props are marked appropriately with ?.


21-26: Properly structured component with clean parameter handling.

The component setup is clean with good destructuring of props and proper retrieval of the workspace slug from router parameters.


27-36: Conditionally rendering CommentCreate based on permissions.

The implementation correctly renders the comment creation form only when editing is allowed, and passes all necessary props to the CommentCreate component.

web/ce/components/comments/comment-block.tsx (4)

11-16: Well-defined type structure for the CommentBlock component.

The component props are clearly defined with appropriate types for each property.


18-25: Component setup with proper null checks.

The component correctly handles cases where the comment or user details are not available by returning an empty fragment.


26-46: Clean UI implementation with responsive styling.

The component includes responsive styling based on the ends prop and properly displays user avatars. The use of conditional styling is well-implemented.


47-70: Well-implemented comment metadata display with tooltip.

The component effectively displays user information and comment timestamps with a helpful tooltip showing formatted date and time. The edited status is also nicely handled.

web/core/components/comments/comment-reaction.tsx (3)

14-18: Clear props definition for the CommentReactions component.

The component props are well-defined with appropriate types for each property.


20-26: Good initialization and null handling.

The component retrieves user reactions and properly returns null when no reactions are found.


27-36: Well-implemented reaction selector component.

The conditional rendering of the ReactionSelector based on disabled state is appropriate, and the component is well-configured with necessary props.

web/core/components/issues/issue-detail/issue-activity/activity-comment-root.tsx (3)

6-7: Updated imports to use new components.

The imports have been appropriately updated to use the new CommentCard component and TCommentsOperations type.


15-24: Type definition updated for TIssueActivityCommentRoot.

The type definition has been properly updated to use TCommentsOperations instead of TActivityOperations, aligning with the new comment operation structure.


38-41: Updated hook destructuring to directly extract getCommentById.

The destructuring from useIssueDetail has been updated to directly access the getCommentById function from the comment object.

web/core/components/comments/comment-create.tsx (3)

2-3: Minor import updates look fine
No issues observed with adding these imports.


20-26: Confirm handling of projectId when absent
The new TCommentCreate type optionally includes projectId, but ensure the rest of the flow gracefully handles scenarios where projectId isn't provided.

Would you like a quick script to find all call sites of <CommentCreate> to confirm that projectId is always passed when needed?


79-79: Nicely handled empty comment check
The isCommentEmpty helper is a clean approach to blocking submissions for empty content.

web/core/components/issues/issue-detail/issue-activity/root.tsx (4)

3-20: Refactored imports are consistent with the new comment architecture
The shift to import CommentCreate and useCommentOperations aligns well with the removal of the old issue-based components.


80-81: Good approach consolidating comment logic
Using const activityOperations = useCommentOperations(...) is a clean way to keep comment features decoupled from the rest of the component logic.


125-130: CommentCreate usage looks consistent
Replacing IssueCommentCreate with CommentCreate while passing entityId, showToolbarInitially, and projectId matches the new design.


51-51:

❓ Verification inconclusive

Ensure you still need this store usage after refactor
It appears you’re pulling issue: { getIssueById } from useIssueDetail; confirm that all references to the old issue store remain necessary.


🏁 Script executed:

#!/bin/bash
# Searching references to getIssueById in the codebase
rg -A 4 'getIssueById'

Length of output: 66680


Double-check the necessity of using the legacy issue store via getIssueById in this component

It looks like you’re still extracting { getIssueById } from useIssueDetail in this file. Since our search confirms that getIssueById is widely used across the codebase—including in this component—please verify that its usage remains intentional after the refactor. In particular, ensure that:

  • The reliance on the legacy issue store isn’t duplicating new store functionalities.
  • No redundant or outdated references persist that could be removed.
web/core/components/comments/comment-card.tsx (5)

19-21: Updated imports reflect new comment component structure
Moving from IssueCommentCard/IssueCommentBlock to CommentBlock aligns with the ongoing refactor.


23-31: Check for undefined comment edge cases
comment can be undefined, so ensure you handle calls that might implicitly assume valid data (e.g., comment.id).


33-66: New CommentCard structure is clearer
Extracting workspace ID from comment and gating with early returns prevents null references. This also simplifies props usage.


139-177: Inline edit logic is well-structured
Using isEditing to toggle between the read-only and editable states, plus hooking the form submission to key events, is a nice approach for user experience.


190-210: CommentReactions integration is seamless
Enabling reactions directly in the comment display fosters quick interactions, with clean toggling of private/public statuses.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (6)
web/ce/components/comments/comment-block.tsx (6)

18-25: Consider adding error handling for userDetails retrieval

The component correctly checks for null/undefined values of comment and userDetails, but there's no error handling if getUserDetails() fails for other reasons. Consider adding a try/catch block or handling error states explicitly.

Also, there's no explanation or fallback UI when returning an empty fragment, which might confuse developers or users.

  const userDetails = getUserDetails(comment?.actor);

- if (!comment || !userDetails) return <></>;
+ // Return a more descriptive fallback or log the reason
+ if (!comment || !userDetails) {
+   console.debug("CommentBlock: Missing comment data or user details");
+   return <></>;
+ }

28-28: Consider using the cn utility for conditional class names

You're using string templating for conditional classes here but using the cn utility elsewhere in the component. For consistency, consider using the cn utility throughout the component.

-      className={`relative flex gap-3 ${ends === "top" ? `pb-2` : ends === "bottom" ? `pt-2` : `py-2`}`}
+      className={cn(
+        "relative flex gap-3",
+        ends === "top" ? "pb-2" : ends === "bottom" ? "pt-2" : "py-2"
+      )}

56-66: Improve timestamp display logic

Currently, you're showing the time based on updated_at while marking edited comments with "(edited)". However, this might be confusing when a comment has been edited as the displayed time would reflect the edit time, not the original creation time. Consider either:

  1. Using created_at for the timestamp display and adding "(edited X ago)" for edits
  2. Clarifying in the tooltip that the time shown is when the comment was last updated

Additionally, consider extracting the hardcoded "commented" text for internationalization.

-            <div className="text-xs text-custom-text-300">
-              commented{" "}
-              <Tooltip
-                tooltipContent={`${renderFormattedDate(comment.created_at)} at ${renderFormattedTime(comment.created_at)}`}
-                position="bottom"
-              >
-                <span className="text-custom-text-350">
-                  {calculateTimeAgo(comment.updated_at)}
-                  {comment.edited_at && " (edited)"}
-                </span>
-              </Tooltip>
-            </div>
+            <div className="text-xs text-custom-text-300">
+              commented{" "}
+              <Tooltip
+                tooltipContent={
+                  `${renderFormattedDate(comment.created_at)} at ${renderFormattedTime(comment.created_at)}` +
+                  (comment.edited_at ? ` (edited on ${renderFormattedDate(comment.edited_at)})` : '')
+                }
+                position="bottom"
+              >
+                <span className="text-custom-text-350">
+                  {calculateTimeAgo(comment.created_at)}
+                  {comment.edited_at && ` (edited ${calculateTimeAgo(comment.edited_at)})`}
+                </span>
+              </Tooltip>
+            </div>

31-34: Enhance accessibility for the timeline indicator

The vertical timeline indicator has aria-hidden which is good, but consider adding more context to the comment structure for screen readers by adding appropriate ARIA attributes to the container.

    <div
-      className={`relative flex gap-3 ${ends === "top" ? `pb-2` : ends === "bottom" ? `pt-2` : `py-2`}`}
+      className={`relative flex gap-3 ${ends === "top" ? `pb-2` : ends === "bottom" ? `pt-2` : `py-2`}`}
+      role="article"
+      aria-label={`Comment from ${userDetails.display_name}`}
      ref={commentBlockRef}
    >

70-70: Consider making the comment content more semantically correct

The comment content is currently wrapped in a simple div with text styling. Consider using a more semantically appropriate element that represents the content better.

-        <div className="text-base mb-2">{children}</div>
+        <section className="text-base mb-2" aria-label="Comment content">{children}</section>

21-21: Unused ref

The commentBlockRef is defined but never used in the component's functionality. If it's intended for future use or for parent component access, consider adding a comment explaining its purpose or remove it if unnecessary.

-  const commentBlockRef = useRef<HTMLDivElement>(null);
+  // Ref used for scrolling to comment or measuring visibility
+  const commentBlockRef = useRef<HTMLDivElement>(null);

Or remove it if it's not needed.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between b4cd1e0 and 430a220.

📒 Files selected for processing (1)
  • web/ce/components/comments/comment-block.tsx (1 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
web/ce/components/comments/comment-block.tsx (1)
packages/types/src/issues/activity/issue_comment.d.ts (1)
  • TIssueComment (17-40)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Analyze (javascript)
  • GitHub Check: Analyze (python)
🔇 Additional comments (1)
web/ce/components/comments/comment-block.tsx (1)

51-54: 🛠️ Refactor suggestion

Add safety check for actor_detail

You're using optional chaining on individual properties of actor_detail but not on the object itself. If actor_detail is undefined, this could cause runtime errors.

-            <div className="text-xs font-medium">
-              {comment?.actor_detail?.is_bot
-                ? comment?.actor_detail?.first_name + " Bot"
-                : comment?.actor_detail?.display_name || userDetails.display_name}
-            </div>
+            <div className="text-xs font-medium">
+              {comment?.actor_detail 
+                ? (comment.actor_detail.is_bot
+                   ? comment.actor_detail.first_name + " Bot"
+                   : comment.actor_detail.display_name)
+                : userDetails.display_name}
+            </div>

Likely an incorrect or invalid review comment.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (6)
packages/i18n/src/locales/en/translations.json (1)

334-335: New Translation Keys Added: "edited" and "bot".
The keys "edited": "Modifié" and "bot": "Bot" have been successfully added. However, please verify that the value for "edited" is correct for the English locale since it is set to a French word ("Modifié"). If this is intentional (for example, to match a design decision or align with other locale mappings), then it's fine; otherwise, consider updating it (e.g., to "Edited").

packages/i18n/src/locales/pl/translations.json (1)

502-503: Localization Keys "edited" and "bot" Added Successfully
The new key-value pairs "edited": "Edytowano" and "bot": "Bot" are clear and consistent with common UI terminology. Ensure that these translations are harmonized with the respective keys in other locale files for consistency across the application.

packages/i18n/src/locales/zh-CN/translations.json (1)

504-505: New Translation Keys Added

The keys "edited": "已编辑" and "bot": "机器人" have been added correctly. Please ensure that these new entries are consistently mirrored in other locale files (as noted in the PR summary) and that the UI components consuming these strings properly reflect the intended meanings.

packages/i18n/src/locales/fr/translations.json (1)

504-505: New Translation Entries for Comments Component
The newly added keys "edited": "Modifié" and "bot": "Bot" provide the required French translations for the comment-related functionality. They are succinct and consistent with the expected terminology. Please ensure these entries are kept in sync with the corresponding entries in the other locale files.

web/core/components/comments/comment-create.tsx (1)

78-78: Simplify null check for commentHTML

The use of ?? with undefined as the fallback is redundant since if commentHTML is null or undefined, this will still evaluate to undefined.

- const isEmpty = isCommentEmpty(commentHTML ?? undefined);
+ const isEmpty = isCommentEmpty(commentHTML);
web/core/components/comments/comment-card.tsx (1)

67-67: Create a clear isDisabled variable for better readability

Define a clear variable for the disabled state to improve code readability.

- const isDisabled = isSubmitting || isEmpty || isSubmitButtonDisabled;
+ // Define this variable with a clearer name for its usage on buttons
+ const isSubmitButtonDisabled = isSubmitting || isEmpty || !isEditorReadyToDiscard;
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 430a220 and 4d76908.

📒 Files selected for processing (18)
  • packages/i18n/src/locales/en/translations.json (1 hunks)
  • packages/i18n/src/locales/es/translations.json (1 hunks)
  • packages/i18n/src/locales/fr/translations.json (1 hunks)
  • packages/i18n/src/locales/it/translations.json (1 hunks)
  • packages/i18n/src/locales/ja/translations.json (1 hunks)
  • packages/i18n/src/locales/ko/translations.json (1 hunks)
  • packages/i18n/src/locales/pl/translations.json (1 hunks)
  • packages/i18n/src/locales/ru/translations.json (1 hunks)
  • packages/i18n/src/locales/sk/translations.json (1 hunks)
  • packages/i18n/src/locales/ua/translations.json (1 hunks)
  • packages/i18n/src/locales/zh-CN/translations.json (1 hunks)
  • packages/i18n/src/locales/zh-TW/translations.json (1 hunks)
  • web/ce/components/comments/comment-block.tsx (1 hunks)
  • web/core/components/comments/comment-card.tsx (6 hunks)
  • web/core/components/comments/comment-create.tsx (5 hunks)
  • web/core/components/comments/comments.tsx (1 hunks)
  • web/core/components/issues/issue-detail/issue-activity/activity-comment-root.tsx (4 hunks)
  • web/core/store/issue/issue-details/comment_reaction.store.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • web/core/store/issue/issue-details/comment_reaction.store.ts
  • web/ce/components/comments/comment-block.tsx
  • web/core/components/comments/comments.tsx
🧰 Additional context used
🧬 Code Definitions (1)
web/core/components/comments/comment-card.tsx (3)
packages/types/src/issues/activity/issue_comment.d.ts (2)
  • TIssueComment (17-40)
  • TCommentsOperations (42-57)
web/ce/components/comments/comment-block.tsx (1)
  • CommentBlock (19-76)
web/core/components/comments/comment-reaction.tsx (1)
  • CommentReactions (20-67)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (16)
packages/i18n/src/locales/ru/translations.json (1)

502-503: Add new translation keys for comment components
The new keys "edited": "Редактировано" and "bot": "Бот" provide the necessary labels for the updated comments UI. The translations are clear and consistent with similar entries in other locale files. Make sure these keys are referenced uniformly across the frontend components related to comment display and interaction.

packages/i18n/src/locales/ko/translations.json (1)

504-505: New Translation Entries for "edited" and "bot".

The additions provide the necessary Korean translations for the comment component updates. The values "수정됨" for "edited" and "봇" for "bot" are consistent with the terminology used in other locale files. Please ensure that these keys are referenced in the codebase wherever the comment update functionality or bot-related displays occur.

packages/i18n/src/locales/es/translations.json (1)

506-507: New Translation Keys Added: "edited" and "bot"

The new entries "edited": "Modificado" and "bot": "Bot" have been introduced to enhance the Spanish localization. They are correctly formatted, follow the naming convention of the JSON file, and appear consistent with similar keys added in other locales.

It would be ideal to double-check that these keys are also consistently referenced in the frontend components that display edited comments and bot indicators.

packages/i18n/src/locales/zh-TW/translations.json (1)

504-505: New Translation Keys Added

The addition of the "edited": "已編輯" and "bot": "機器人" entries is clear and straightforward. These keys are consistent with the similar entries added in other locale files, ensuring a uniform translation experience across the application. Please verify that these translations semantically match their intended usage in the UI.

packages/i18n/src/locales/sk/translations.json (1)

502-503: New Translation Entries Added for "edited" and "bot".
The two new keys "edited": "Upravené" and "bot": "Bot" are correctly added and consistent with the translations in similar locale files. This enhances our Slovak localization for comment metadata.

packages/i18n/src/locales/it/translations.json (1)

503-504: New Translation Entries for Comments Component

The addition of "edited": "Modificato" and "bot": "Bot" is clear and consistent with similar keys in other locale files. These entries will support the updated comments interface by distinguishing edited content and bot-generated messages.

packages/i18n/src/locales/ja/translations.json (1)

504-505: Added Translation Entries for Comments Refactor.

The new entries "edited": "編集済み" and "bot": "ボット" are correctly added and formatted. They align with the corresponding changes in other locale files, ensuring consistency across the application.

packages/i18n/src/locales/ua/translations.json (1)

502-504: New Translation Keys Added for Comments Functionality

The keys "edited": "Редагувано" and "bot": "Бот" have been correctly introduced and are consistent with the style and naming conventions used throughout the file. These entries will support UI updates related to comment edits and bot functionalities as outlined by the PR objectives.

web/core/components/issues/issue-detail/issue-activity/activity-comment-root.tsx (2)

51-53: Store the comment in a variable before the conditional check

The comment object is fetched inside the map function and then immediately used in a conditional. This could lead to redundant lookups if the comment is referenced multiple times.

- {filteredActivityComments.map((activityComment, index) => {
-   const comment = getCommentById(activityComment.id);
-   return activityComment.activity_type === "COMMENT" ? (
+ {filteredActivityComments.map((activityComment, index) => {
+   const comment = getCommentById(activityComment.id);
+   return activityComment.activity_type === "COMMENT" ? (

6-7: LGTM: Type and component updates properly refactored

The changes to types, imports, and component props look good. The refactoring replaces IssueCommentCard with the more generic CommentCard and updates type definitions accordingly.

Also applies to: 20-20, 40-40, 57-58, 62-62

web/core/components/comments/comment-create.tsx (3)

52-75: Avoid mixing await and .then() in the same call

It's clearer to consistently use async/await with try/catch instead of mixing them with promise chaining for better readability and error handling.

- activityOperations
-   .createComment(formData)
-   .then(async () => {
-     if (uploadedAssetIds.length > 0) {
-       if (projectId) {
-         await fileService.updateBulkProjectAssetsUploadStatus(workspaceSlug, projectId.toString(), entityId, {
-           asset_ids: uploadedAssetIds,
-         });
-       } else {
-         await fileService.updateBulkWorkspaceAssetsUploadStatus(workspaceSlug, entityId, {
-           asset_ids: uploadedAssetIds,
-         });
-       }
-       setUploadedAssetIds([]);
-     }
-   })
-   .finally(() => {
-     reset({
-       comment_html: "<p></p>",
-     });
-     editorRef.current?.clearEditor();
-   });

+ try {
+   await activityOperations.createComment(formData);
+   if (uploadedAssetIds.length > 0) {
+     if (projectId) {
+       await fileService.updateBulkProjectAssetsUploadStatus(workspaceSlug, projectId.toString(), entityId, {
+         asset_ids: uploadedAssetIds,
+       });
+     } else {
+       await fileService.updateBulkWorkspaceAssetsUploadStatus(workspaceSlug, entityId, {
+         asset_ids: uploadedAssetIds,
+       });
+     }
+     setUploadedAssetIds([]);
+   }
+ } finally {
+   reset({
+     comment_html: "<p></p>",
+   });
+   editorRef.current?.clearEditor();
+ }

19-30: LGTM: Type definition properly updated to generalize comments

The change from TIssueCommentCreate to TCommentCreate with entityId instead of issueId makes the component more reusable across different entities that can have comments.


56-65: LGTM: Asset upload handling correctly updated

The code correctly handles asset uploads for both project-specific and workspace-level contexts based on whether projectId is available.

web/core/components/comments/comment-card.tsx (3)

171-174: Use the cn helper for complex className logic

The complex conditional className strings would be more readable using the imported cn helper function.

- disabled={isDisabled}
- className={`group rounded border border-green-500 bg-green-500/20 p-2 shadow-md duration-300  ${
-   isEmpty ? "cursor-not-allowed bg-gray-200" : "hover:bg-green-500"
- }`}
+ disabled={isDisabled}
+ className={cn(
+   "group rounded border border-green-500 bg-green-500/20 p-2 shadow-md duration-300",
+   isEmpty ? "cursor-not-allowed bg-gray-200" : "hover:bg-green-500"
+ )}

208-208: Don't use type assertion, use toString() instead

Type assertion with as is not the recommended way to handle optional values. Use the toString() method for consistency with other parts of the codebase.

- projectId={(projectId as string) ?? ""}
+ projectId={projectId?.toString() ?? ""}

10-11: LGTM: Component structure properly refactored

The component has been successfully refactored to use the new CommentBlock component and handle comment reactions with CommentReactions. The changes make the code more maintainable and consistent with the rest of the application.

Also applies to: 19-21, 23-26, 36-37, 62-63, 87-88, 190-211

@gakshita gakshita changed the title fix: comments refactor [WEB-3698] fix: comments refactor Mar 27, 2025
Copy link

makeplane bot commented Mar 27, 2025

Pull Request Linked with Plane Work Items

Comment Automatically Generated by Plane

@sriramveeraghanta sriramveeraghanta merged commit 869c755 into preview Mar 27, 2025
5 of 6 checks passed
@sriramveeraghanta sriramveeraghanta deleted the chore-comment-reactions branch March 27, 2025 11:58
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.

4 participants