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

Added delete action and dialog to ActivityPub feed item #22356

Merged
merged 4 commits into from
Mar 5, 2025

Conversation

minimaluminium
Copy link
Member

ref AP-817, AP-818, AP-819

  • added a new component AlertDialog to Shade
  • replaced the old Menu component with Popover from Shade for the feed item menu
  • added delete option to the menu
  • added a confirmation dialog to the delete action

Copy link
Contributor

coderabbitai bot commented Mar 4, 2025

Walkthrough

The pull request refactors the menu handling within the FeedItem component by introducing a new FeedItemMenu component, which simplifies the interaction model for actions like copying a link and deleting a post. The previous menuIsOpen state and menuItems array have been removed. The Button component has been renamed to ButtonX. Additionally, a new alert dialog component has been created using the Radix UI library, which includes various subcomponents for managing alert dialogs. Minor changes include enabling GPU acceleration for dialog overlays and correcting CSS syntax errors by removing extraneous semicolons. Dependency updates in package.json support the new UI features.

Possibly related PRs

  • Added dark mode support to ActivityPub #22228: The changes in the main PR involve refactoring the FeedItem component and introducing a new FeedItemMenu, which relates to modifications in the retrieved PR that also targets the FeedItem.tsx file.
  • ActivityPub UI restructure #22209: The changes in the main PR involve a refactor of the FeedItem component and the introduction of a new FeedItemMenu component, which is related to styling adjustments in the retrieved PR affecting the same component.
  • Reader improvements in ActivityPub #22264: The changes in the main PR are related to the introduction and refactoring of the FeedItemMenu component, which involves similar UI component structures and interactions as the modifications in the retrieved PR that refactors popover functionality in the ArticleModal component.

Suggested reviewers

  • peterzimon

Warning

There were issues while running some tools. Please review the errors and either fix the tool’s configuration or disable the tool if it’s a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

apps/admin-x-activitypub/src/components/feed/FeedItem.tsx

Oops! Something went wrong! :(

ESLint: 8.44.0

ESLint couldn't find the plugin "eslint-plugin-react-hooks".

(The package "eslint-plugin-react-hooks" was not found when loaded as a Node module from the directory "/apps/admin-x-activitypub".)

It's likely that the plugin isn't installed correctly. Try reinstalling by running the following:

npm install eslint-plugin-react-hooks@latest --save-dev

The plugin "eslint-plugin-react-hooks" was referenced from the config file in "apps/admin-x-activitypub/.eslintrc.cjs".

If you still can't figure out the problem, please stop by https://eslint.org/chat/help to chat with the team.

apps/shade/src/components/ui/alert-dialog.tsx

Oops! Something went wrong! :(

ESLint: 8.44.0

ESLint couldn't find the plugin "eslint-plugin-react-hooks".

(The package "eslint-plugin-react-hooks" was not found when loaded as a Node module from the directory "/apps/shade".)

It's likely that the plugin isn't installed correctly. Try reinstalling by running the following:

npm install eslint-plugin-react-hooks@latest --save-dev

The plugin "eslint-plugin-react-hooks" was referenced from the config file in "apps/shade/.eslintrc.cjs".

If you still can't figure out the problem, please stop by https://eslint.org/chat/help to chat with the team.

apps/shade/src/components/ui/popover.tsx

Oops! Something went wrong! :(

ESLint: 8.44.0

ESLint couldn't find the plugin "eslint-plugin-react-hooks".

(The package "eslint-plugin-react-hooks" was not found when loaded as a Node module from the directory "/apps/shade".)

It's likely that the plugin isn't installed correctly. Try reinstalling by running the following:

npm install eslint-plugin-react-hooks@latest --save-dev

The plugin "eslint-plugin-react-hooks" was referenced from the config file in "apps/shade/.eslintrc.cjs".

If you still can't figure out the problem, please stop by https://eslint.org/chat/help to chat with the team.

  • 4 others

📜 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 6da42a9 and 139a638.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (8)
  • apps/admin-x-activitypub/src/components/feed/FeedItem.tsx (8 hunks)
  • apps/admin-x-activitypub/src/components/feed/FeedItemMenu.tsx (1 hunks)
  • apps/shade/package.json (2 hunks)
  • apps/shade/src/components/ui/alert-dialog.tsx (1 hunks)
  • apps/shade/src/components/ui/dialog.tsx (1 hunks)
  • apps/shade/src/components/ui/popover.tsx (2 hunks)
  • apps/shade/src/index.ts (1 hunks)
  • apps/shade/styles.css (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • apps/shade/src/components/ui/dialog.tsx
  • apps/shade/package.json
  • apps/shade/src/index.ts
  • apps/shade/styles.css
  • apps/shade/src/components/ui/popover.tsx
  • apps/admin-x-activitypub/src/components/feed/FeedItemMenu.tsx
  • apps/shade/src/components/ui/alert-dialog.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
apps/admin-x-activitypub/src/components/feed/FeedItem.tsx

[error] 418-418: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

🔇 Additional comments (6)
apps/admin-x-activitypub/src/components/feed/FeedItem.tsx (6)

1-1: New component integration looks good

The new FeedItemMenu component is properly imported, which aligns with the PR objectives of improving the feed item menu functionality.


240-244: Shade Button implementation looks correct

You've successfully implemented the Button component from Shade, which aligns with the modernization effort mentioned in the PR objectives.


279-283: Feed item menu implementation looks good

The FeedItemMenu component is correctly implemented, passing the appropriate trigger and callback functions. This matches the PR objective of replacing the Menu component with a more modern alternative.


371-375: Consistent FeedItemMenu implementations

You've consistently implemented the FeedItemMenu component across all layout variants (modal, reply, inbox). This is good practice and ensures a consistent user experience.

Also applies to: 408-412, 503-507


1-522:

✅ Verification successful

Verify the confirmation dialog implementation

The PR objective mentions adding a confirmation dialog for deletions, but I can't see how that's implemented in this file. It appears the dialog might be implemented within the FeedItemMenu component, but we should verify this.


🏁 Script executed:

#!/bin/bash
# Description: Check the implementation of FeedItemMenu for a confirmation dialog

# Check the FeedItemMenu component for dialog implementation
if [ -f apps/admin-x-activitypub/src/components/feed/FeedItemMenu.tsx ]; then
  echo "Checking FeedItemMenu.tsx for dialog implementation:"
  cat apps/admin-x-activitypub/src/components/feed/FeedItemMenu.tsx
else
  echo "FeedItemMenu.tsx file not found"
  
  # Search for possible locations of the file
  echo "Searching for FeedItemMenu file:"
  find apps/admin-x-activitypub/src -name "FeedItemMenu.*"
fi

Length of output: 3532


Confirmation dialog is correctly implemented in FeedItemMenu

The deletion confirmation dialog is implemented within the FeedItemMenu component. The component uses AlertDialog (and related) components from @tryghost/shade to display a confirmation dialog that asks, "Are you sure you want to delete this post?" before invoking the onDelete callback. This implementation addresses the PR objective, so no further changes are needed in FeedItem.tsx regarding the confirmation dialog.

  • Verified that FeedItemMenu.tsx includes the AlertDialog, AlertDialogTrigger, AlertDialogContent, AlertDialogAction, and AlertDialogCancel components.
  • Confirmed that the delete action is wrapped inside the confirmation dialog, ensuring safe deletion approval.
🧰 Tools
🪛 Biome (1.9.4)

[error] 298-298: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)


[error] 359-359: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)


[error] 418-418: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)


[error] 475-475: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)


[error] 482-482: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)


299-300:

❓ Verification inconclusive

Fix all dangerouslySetInnerHTML instances

There are multiple instances of dangerouslySetInnerHTML throughout the component that need to be sanitized to protect against XSS attacks.

Also applies to: 359-359, 476-478, 482-484


🏁 Script executed:

#!/bin/bash
# Description: Search for unprotected dangerouslySetInnerHTML instances

# Check for sanitization functions in the repo
echo "Checking for HTML sanitization functions:"
rg -A 3 -B 3 'sanitize(Html|Content)' --type=typescript

# Count all dangerouslySetInnerHTML instances
echo -e "\nCounting dangerouslySetInnerHTML instances:"
rg --count 'dangerouslySetInnerHTML' --type=typescript apps/admin-x-activitypub/src

# Check if we import any sanitization libraries
echo -e "\nChecking for sanitization library imports:"
rg "import.*from '(dompurify|sanitize-html|xss)'" --type=typescript apps/admin-x-activitypub/src

Length of output: 648


Below is the rewritten review comment. Since our initial checks using the --type=typescript flag did not return expected results (due to file‐type recognition issues), please re-run verification manually or use a revised script (e.g., without the type flag) to confirm that no HTML sanitization is applied. In the meantime, the comment below reflects that unsanitized uses of dangerouslySetInnerHTML persist in multiple locations.


Critical: Sanitize All HTML Rendering to Prevent XSS
It appears that the component in apps/admin-x-activitypub/src/components/feed/FeedItem.tsx continues to render HTML via multiple instances of dangerouslySetInnerHTML (specifically at lines 299–300, 359, 476–478, and 482–484) without any indication of HTML sanitization. This raises a significant security risk. Please ensure that all content injected via dangerouslySetInnerHTML is properly sanitized (for example, by using libraries such as dompurify, sanitize-html, or xss) to protect against cross-site scripting (XSS) attacks.

  • Action Required:
    • Either implement a sanitization routine at each usage or refactor to a safer alternative for HTML rendering.
    • Manually verify that no other parts of the codebase bypass sanitization, especially in dynamically rendered content.
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • 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 configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

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

CodeRabbit Configuration File (.coderabbit.yaml)

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

Documentation and Community

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
apps/admin-x-activitypub/src/components/feed/FeedItemMenu.tsx (1)

70-72: Consider revising the confirmation message tone.

The confirmation message ends with a casual "We warned you, k?" which may not be appropriate for all users or contexts. Consider using a more professional tone for the alert dialog.

-                        You&apos;re about to delete this post. This is permanent! We warned you, k?
+                        You&apos;re about to delete this post. This action is permanent and cannot be undone.
apps/shade/src/components/ui/alert-dialog.tsx (1)

13-26: Well-implemented overlay with GPU acceleration.

The overlay component:

  • Correctly uses forwardRef for proper ref handling
  • Implements nice visual effects with backdrop blur
  • Uses GPU acceleration with transform-gpu for better performance
  • Has appropriate animations for state transitions

However, there's a small inconsistency in the blur settings:

-            'fixed inset-0 z-50 bg-black/30 backdrop-blur-none transform-gpu data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=open]:backdrop-blur-[3px]',
+            'fixed inset-0 z-50 bg-black/30 transform-gpu data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:backdrop-blur-none data-[state=open]:backdrop-blur-[3px]',
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 4dff449 and fae5eb3.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (8)
  • apps/admin-x-activitypub/src/components/feed/FeedItem.tsx (8 hunks)
  • apps/admin-x-activitypub/src/components/feed/FeedItemMenu.tsx (1 hunks)
  • apps/shade/package.json (2 hunks)
  • apps/shade/src/components/ui/alert-dialog.tsx (1 hunks)
  • apps/shade/src/components/ui/dialog.tsx (1 hunks)
  • apps/shade/src/components/ui/popover.tsx (2 hunks)
  • apps/shade/src/index.ts (1 hunks)
  • apps/shade/styles.css (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • apps/shade/src/components/ui/dialog.tsx
  • apps/shade/styles.css
🧰 Additional context used
🪛 Biome (1.9.4)
apps/admin-x-activitypub/src/components/feed/FeedItem.tsx

[error] 424-424: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Setup
🔇 Additional comments (26)
apps/shade/src/index.ts (1)

2-2: LGTM: Successfully exported the new AlertDialog component.

The new export line allows the AlertDialog component to be used throughout the project, which is necessary for implementing the confirmation dialog mentioned in the PR objectives.

apps/shade/package.json (2)

68-68: LGTM: Added Radix UI AlertDialog dependency.

This dependency is correctly added to support the new AlertDialog component for the delete confirmation functionality.


77-77: LGTM: Updated @radix-ui/react-slot dependency.

The version update from 1.1.1 to ^1.1.2 ensures compatibility with the other Radix UI components being used.

apps/shade/src/components/ui/popover.tsx (2)

12-12: LGTM: Added PopoverClose component.

The PopoverClose component from Radix UI is correctly defined, which will enable dismissing popovers programmatically.


31-31: LGTM: Updated exports to include PopoverClose.

Properly updated the export statement to include the new PopoverClose component, making it available for consumption by other components.

apps/admin-x-activitypub/src/components/feed/FeedItem.tsx (8)

1-1: LGTM: Imported the new FeedItemMenu component.

This aligns with the PR objectives of abstracting menu behavior into a separate component.


4-4: LGTM: Renamed Button import to ButtonX.

The import was updated to reflect the component rename mentioned in the PR summary.


241-242: LGTM: Updated to use ButtonX component.

Correctly uses the renamed ButtonX component in the UserMenuTrigger.


285-289: LGTM: Implemented FeedItemMenu in feed layout.

Successfully replaced the previously hardcoded menu implementation with the new FeedItemMenu component, passing appropriate props for the feed layout.


377-381: LGTM: Implemented FeedItemMenu in modal layout.

Successfully incorporated the FeedItemMenu component in the modal layout view.


414-418: LGTM: Implemented FeedItemMenu in reply layout.

Successfully incorporated the FeedItemMenu component in the reply layout view.


426-426: LGTM: Updated Button to ButtonX in render output.

Consistently used the renamed ButtonX component throughout the component.


509-513: LGTM: Implemented FeedItemMenu in inbox layout.

Successfully incorporated the FeedItemMenu component in the inbox layout view.

apps/admin-x-activitypub/src/components/feed/FeedItemMenu.tsx (6)

1-17: Good component organization with comprehensive imports.

The imports are well-organized, importing all necessary components from the @tryghost/shade UI library, including the new AlertDialog components.


19-23: Clean interface definition with appropriate props.

The FeedItemMenuProps interface is well-defined with the necessary properties:

  • trigger: Allows for flexible UI triggering element
  • onCopyLink and onDelete: Clear callback functions for the actions

This is a good example of component API design with clear separation of concerns.


30-38: Good event handling with stopPropagation.

Both event handlers correctly prevent event bubbling by calling e.stopPropagation() before executing their respective callbacks, which prevents unintended side effects when these actions are triggered.


43-45: Event propagation is properly handled in the trigger.

Good use of stopPropagation to prevent the click event from affecting parent elements when the menu is triggered.


46-66: Well-structured popover menu with clear actions.

The popover content is well organized with:

  • Proper alignment using align='end'
  • Clear action buttons with appropriate styling
  • Properly nested components to handle both the popover and alert dialog interactions

74-84: Well-implemented alert dialog footer with appropriate button styling.

The footer properly implements:

  • Cancel and Delete buttons with correct positioning
  • Appropriate event handling for the Delete button
  • Destructive styling for the Delete button using buttonVariants
  • Event propagation control
apps/shade/src/components/ui/alert-dialog.tsx (7)

1-6: Good imports and dependency setup.

The imports are well organized, correctly importing from Radix UI for the alert dialog primitives and local utility functions.


7-11: Clean re-export of Radix primitives.

Exporting the primitives directly makes the implementation cleaner and more maintainable.


48-74: Good layout components for the dialog.

The header and footer components:

  • Have clear, semantic names
  • Use appropriate flex layouts that adapt to different screen sizes
  • Allow for customization via className prop
  • Handle responsive design (column on mobile, row on larger screens for footer)

76-99: Well-implemented Title and Description components.

Both components:

  • Correctly use forwardRef
  • Have appropriate default styling
  • Pass through additional props
  • Set display names for better debugging

101-127: Action buttons with appropriate styling.

The Action and Cancel buttons:

  • Use appropriate button variants (primary and outline)
  • Handle responsiveness with mobile-specific margins
  • Allow for className overrides
  • Correctly use forwardRef for proper ref handling

129-141: Comprehensive exports for the dialog components.

All components are properly exported, making them available for use throughout the application.


28-45:

✅ Verification successful

Well-structured dialog content with animations.

The AlertDialogContent component:

  • Has proper positioning (centered on screen)
  • Includes responsive styling with SM breakpoint
  • Uses appropriate animations for transitions
  • Properly stops event propagation on the overlay

However, consider examining the need for the extra shade div:


🏁 Script executed:

#!/bin/bash
# Check if the 'shade' class is used elsewhere or defined in the codebase

echo "Checking for the shade class definition..."
rg -A 2 -B 2 "\.shade\s*{" --type css --type scss --type less

echo "Checking for other uses of the shade className..."
rg "className=.*shade" --type tsx --type jsx

Length of output: 428


The previous commands ran into regex errors and unrecognized file type issues. I've updated the script to escape special characters properly and use glob patterns for TSX/JSX files. Please run these new commands and verify whether the extra <div className="shade"> is needed based on its definition or usage in your codebase.

Review Note: The component is otherwise well-structured (proper centering, responsive styling, animations, and event handling). Confirm the necessity of the extra shade div once you have the output from the above script.


🏁 Script executed:

#!/bin/bash
echo "Re-checking for the 'shade' class definition in CSS/SCSS/LESS files..."
rg --regexp '\\.shade\\s*{' --type css --type scss --type less

echo "Re-checking for the usage of 'shade' in React files (TSX/JSX) using glob patterns..."
rg 'className=.*shade' --glob '*.tsx' --glob '*.jsx'

Length of output: 1153


AlertDialogContent Component: Confirming Consistency in the 'shade' Wrapper

The component is well-structured—with proper centering, responsive styling, smooth animations, and effective event handling. Our investigation shows that the extra <div className="shade"> wrapper is used consistently across the codebase (e.g., in apps/admin-x-activitypub/src/App.tsx, and multiple instances in the apps/shade/src/components/ui folder such as in alert-dialog.tsx, dialog.tsx, dropdown-menu.tsx, and tooltip.tsx). This indicates that its inclusion is likely intentional to maintain consistent styling or layout behavior.

Next Steps:

  • Please confirm that the .shade class has the appropriate style definitions in your global or modular CSS. If the styling isn’t defined or applied as intended, consider revisiting its necessity or consolidating the pattern.
  • If the styles are defined and the extra wrapper meets design requirements, no changes are needed.

trigger={UserMenuTrigger}
onCopyLink={handleCopyLink}
onDelete={handleDelete}
/>
</div>
<div className={`relative z-10 col-start-2 col-end-3 w-full gap-4`}>
<div className='flex flex-col'>
{(object.type === 'Article') && renderFeedAttachment(object, layout)}
{object.name && <Heading className='my-1 text-pretty leading-tight' level={5} data-test-activity-heading>{object.name}</Heading>}
{(object.preview && object.type === 'Article') ? <div className='line-clamp-3 leading-tight'>{object.preview.content}</div> : <div dangerouslySetInnerHTML={({__html: object.content ?? ''})} className='ap-note-content text-pretty tracking-[-0.006em] text-gray-900 dark:text-gray-600'></div>}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Address XSS vulnerability with dangerouslySetInnerHTML.

Using dangerouslySetInnerHTML directly with content from external sources (like ActivityPub objects) exposes users to cross-site scripting (XSS) attacks.

Consider implementing content sanitization before rendering:

-<div dangerouslySetInnerHTML={({__html: object.content ?? ''})} className='ap-note-content text-pretty tracking-[-0.006em] text-gray-900 dark:text-gray-600'></div>
+<div dangerouslySetInnerHTML={({__html: sanitizeHtml(object.content ?? '')})} className='ap-note-content text-pretty tracking-[-0.006em] text-gray-900 dark:text-gray-600'></div>

You'll need to import a sanitization library like DOMPurify:

import DOMPurify from 'dompurify';

// Define a sanitization function
const sanitizeHtml = (html) => {
  return DOMPurify.sanitize(html, {
    ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p', 'br'],
    ALLOWED_ATTR: ['href', 'target', 'rel']
  });
};

Apply this same pattern to all other instances of dangerouslySetInnerHTML in the component.

🧰 Tools
🪛 Biome (1.9.4)

[error] 424-424: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

Comment on lines +228 to +230
const handleDelete = () => {

menuItems.push({
id: 'copy-link',
label: 'Copy link to post',
onClick: handleCopyLink
});
};
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Implement the delete handler function.

The handleDelete function is currently empty. To fulfill the PR objective of adding delete functionality to feed items, this function needs to be implemented.

const handleDelete = () => {
+   // API call to delete the feed item
+   // Example implementation:
+   // deleteActivityPubObject(object.id)
+   //   .then(() => {
+   //     showToast({
+   //       title: 'Item deleted successfully',
+   //       type: 'success'
+   //     });
+   //   })
+   //   .catch((error) => {
+   //     showToast({
+   //       title: 'Failed to delete item',
+   //       description: error.message,
+   //       type: 'error'
+   //     });
+   //   });
};
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const handleDelete = () => {
menuItems.push({
id: 'copy-link',
label: 'Copy link to post',
onClick: handleCopyLink
});
};
const handleDelete = () => {
// API call to delete the feed item
// Example implementation:
// deleteActivityPubObject(object.id)
// .then(() => {
// showToast({
// title: 'Item deleted successfully',
// type: 'success'
// });
// })
// .catch((error) => {
// showToast({
// title: 'Failed to delete item',
// description: error.message,
// type: 'error'
// });
// });
};

@@ -250,8 +238,8 @@ const FeedItem: React.FC<FeedItemProps> = ({actor, object, layout, type, comment
// });

const UserMenuTrigger = (
<Button
className={`transition-color relative z-[9998] flex h-[34px] w-[34px] items-center justify-center ${layout === 'feed' ? 'rounded-full' : 'rounded-md'} bg-white hover:bg-gray-100 dark:bg-black dark:hover:bg-gray-950 ${(layout === 'feed' || layout === 'modal') && 'ml-auto'}`}
<ButtonX
Copy link
Contributor

Choose a reason for hiding this comment

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

We should replace and use the Button component from Shade instead of the old system. What's the reason to keep the old one here?

} from '@tryghost/shade';

interface FeedItemMenuProps {
trigger: React.ReactNode;
Copy link
Contributor

Choose a reason for hiding this comment

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

I really like this pattern of passing the trigger to the dialog! What do you think about instead of passing on a ReactNode component as a prop, so the menu could be used like this:

<FeedItemMenu onCopyLink={...} onDelete={...}>
   <Button>Open menu</Button>
</FeedItemMenu>

Does this make sense to you?

@minimaluminium minimaluminium force-pushed the ap-feed-item-menu-AP-819 branch from fae5eb3 to dad596d Compare March 5, 2025 01:11
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: 2

♻️ Duplicate comments (1)
apps/admin-x-activitypub/src/components/feed/FeedItem.tsx (1)

228-230: ⚠️ Potential issue

Implement the delete handler function.

The handleDelete function is currently empty. To fulfill the PR objective of adding delete functionality to feed items, this function needs to be implemented.

Consider implementing the delete functionality similar to:

const handleDelete = () => {
+   // API call to delete the feed item
+   if (object?.id) {
+       // Call your delete API here, e.g.:
+       // deleteActivityPubObject(object.id)
+       //   .then(() => {
+       //     showToast({
+       //       title: 'Item deleted successfully',
+       //       type: 'success'
+       //     });
+       //   })
+       //   .catch((error) => {
+       //     showToast({
+       //       title: 'Failed to delete item',
+       //       description: error.message,
+       //       type: 'error'
+       //     });
+       //   });
+   }
};
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between fae5eb3 and dad596d.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (8)
  • apps/admin-x-activitypub/src/components/feed/FeedItem.tsx (8 hunks)
  • apps/admin-x-activitypub/src/components/feed/FeedItemMenu.tsx (1 hunks)
  • apps/shade/package.json (2 hunks)
  • apps/shade/src/components/ui/alert-dialog.tsx (1 hunks)
  • apps/shade/src/components/ui/dialog.tsx (1 hunks)
  • apps/shade/src/components/ui/popover.tsx (2 hunks)
  • apps/shade/src/index.ts (1 hunks)
  • apps/shade/styles.css (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • apps/shade/src/components/ui/dialog.tsx
  • apps/shade/src/components/ui/popover.tsx
  • apps/shade/styles.css
  • apps/shade/src/index.ts
  • apps/shade/package.json
  • apps/admin-x-activitypub/src/components/feed/FeedItemMenu.tsx
  • apps/shade/src/components/ui/alert-dialog.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
apps/admin-x-activitypub/src/components/feed/FeedItem.tsx

[error] 424-424: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Setup
🔇 Additional comments (6)
apps/admin-x-activitypub/src/components/feed/FeedItem.tsx (6)

1-1: Good refactoring approach with the new FeedItemMenu component!

The extraction of menu functionality into a dedicated FeedItemMenu component is a good architectural decision that improves modularity and code organization.


285-289: Good implementation of the FeedItemMenu component!

The refactoring to use a dedicated menu component improves code organization and reusability. The menu component receives the trigger element and callback functions, which is a clean approach.


304-306: ⚠️ Potential issue

Address XSS vulnerability with dangerouslySetInnerHTML.

Using dangerouslySetInnerHTML directly with content from external sources (like ActivityPub objects) exposes users to cross-site scripting (XSS) attacks.

Consider implementing content sanitization before rendering:

-<div dangerouslySetInnerHTML={{
-    __html: object.content ?? ''
-}} ref={contentRef} />
+<div dangerouslySetInnerHTML={{
+    __html: sanitizeHtml(object.content ?? '')
+}} ref={contentRef} />

You'll need to import a sanitization library like DOMPurify:

import DOMPurify from 'dompurify';

// Define a sanitization function
const sanitizeHtml = (html) => {
  return DOMPurify.sanitize(html, {
    ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p', 'br'],
    ALLOWED_ATTR: ['href', 'target', 'rel']
  });
};

Apply this same pattern to all other instances of dangerouslySetInnerHTML in the component (lines 365, 424, 482, 489).

🧰 Tools
🪛 Biome (1.9.4)

[error] 304-304: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)


377-381: Good implementation of the FeedItemMenu in modal layout!

Consistent implementation of the menu component across different layouts improves maintainability.


414-418: Good implementation of the FeedItemMenu in reply layout!

Consistent implementation of the menu component across different layouts improves maintainability.


509-513: Good implementation of the FeedItemMenu in inbox layout!

Consistent implementation of the menu component across different layouts improves maintainability.

import React, {useEffect, useRef, useState} from 'react';
import {ActorProperties, ObjectProperties} from '@tryghost/admin-x-framework/api/activitypub';
import {Button, Heading, Icon, Menu, MenuItem, showToast} from '@tryghost/admin-x-design-system';
import {Button as ButtonX, Heading, Icon, showToast} from '@tryghost/admin-x-design-system';
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Rename from Button to ButtonX needs clarification.

The renamed import suggests a transition period or potential confusion with component naming. Per the previous reviewer comment, consider using the Button component from Shade directly instead of using this renamed approach.


🏁 Script executed:

#!/bin/bash
# Check if there's a Button component in the Shade library that should be used instead
find ./apps/shade/src -type f -name "*.tsx" | xargs grep -l "export.*Button"

Length of output: 120


Clarify the Button Source and Renaming Approach

The current import aliases the design system’s Button as ButtonX, but our verification confirms that a Button component exists directly in the Shade library (see apps/shade/src/components/ui/button.tsx). Please clarify if the alias is intended for a transitional period or if we should update the code to import the Button component from Shade directly.

</div>
<div className={`relative z-10 col-start-2 col-end-3 w-full gap-4`}>
<div className='flex flex-col'>
{(object.type === 'Article') && renderFeedAttachment(object, layout)}
{object.name && <Heading className='my-1 text-pretty leading-tight' level={5} data-test-activity-heading>{object.name}</Heading>}
{(object.preview && object.type === 'Article') ? <div className='line-clamp-3 leading-tight'>{object.preview.content}</div> : <div dangerouslySetInnerHTML={({__html: object.content ?? ''})} className='ap-note-content text-pretty tracking-[-0.006em] text-gray-900 dark:text-gray-600'></div>}
{(object.type === 'Note') && renderFeedAttachment(object, layout)}
{(object.type === 'Article') && <Button
{(object.type === 'Article') && <ButtonX
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

❓ Verification inconclusive

Button component renamed but not replaced.

The PR description indicates that the Menu component was replaced with Popover from Shade, but the Button component wasn't fully replaced with its Shade equivalent as suggested in the previous review.


🏁 Script executed:

#!/bin/bash
# Check if there's a ButtonX component defined in the design system
find ./apps -type f -name "*.tsx" | xargs grep -l "export.*ButtonX"

Length of output: 69


Action Required: Replace legacy ButtonX with the new Shade button component

The usage of ButtonX at line 426 in apps/admin-x-activitypub/src/components/feed/FeedItem.tsx remains unchanged. Our earlier verification confirms that there is no export for ButtonX in the design system, which aligns with previous concerns. Please update this instance to use the appropriate Shade button component as indicated in the PR description (similar to the Menu component’s replacement with Shade’s Popover).

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

🔭 Outside diff range comments (1)
apps/admin-x-activitypub/src/components/feed/FeedItem.tsx (1)

420-426: 🛠️ Refactor suggestion

Replace ButtonX with Shade Button component

Per previous reviewer comments and your other changes, you should replace the remaining instance of ButtonX with the Button component from Shade.

-{(object.type === 'Article') && <ButtonX
-    className={`mt-3 self-start text-gray-900 transition-all hover:opacity-60`}
-    color='grey'
-    fullWidth={true}
-    id='read-more'
-    label='Read more'
-    size='md'
-/>}
+{(object.type === 'Article') && <Button
+    className="mt-3 self-start text-gray-900 transition-all hover:opacity-60"
+    fullWidth
+    id="read-more"
+    size="md"
+    variant="secondary"
+>
+    Read more
+</Button>}
♻️ Duplicate comments (3)
apps/admin-x-activitypub/src/components/feed/FeedItem.tsx (3)

228-230: ⚠️ Potential issue

Implement the delete handler function

The handleDelete function is currently empty. To fulfill the PR objective of adding delete functionality to ActivityPub feed items, this function needs to be implemented.

const handleDelete = () => {
+   // API call to delete the feed item
+   // Example implementation:
+   // deleteActivityPubObject(object.id)
+   //   .then(() => {
+   //     showToast({
+   //       title: 'Item deleted successfully',
+   //       type: 'success'
+   //     });
+   //   })
+   //   .catch((error) => {
+   //     showToast({
+   //       title: 'Failed to delete item',
+   //       description: error.message,
+   //       type: 'error'
+   //     });
+   //   });
};

418-418: ⚠️ Potential issue

Address XSS vulnerability with dangerouslySetInnerHTML

Using dangerouslySetInnerHTML directly with content from external sources (like ActivityPub objects) exposes users to cross-site scripting (XSS) attacks.

Consider implementing content sanitization before rendering:

-<div dangerouslySetInnerHTML={({__html: object.content ?? ''})} className='ap-note-content text-pretty tracking-[-0.006em] text-gray-900 dark:text-gray-600'></div>
+<div dangerouslySetInnerHTML={({__html: sanitizeHtml(object.content ?? '')})} className='ap-note-content text-pretty tracking-[-0.006em] text-gray-900 dark:text-gray-600'></div>

You'll need to import a sanitization library like DOMPurify:

import DOMPurify from 'dompurify';

// Define a sanitization function
const sanitizeHtml = (html) => {
  return DOMPurify.sanitize(html, {
    ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p', 'br'],
    ALLOWED_ATTR: ['href', 'target', 'rel']
  });
};

Apply this same pattern to all other instances of dangerouslySetInnerHTML in the component.

🧰 Tools
🪛 Biome (1.9.4)

[error] 418-418: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)


4-5: 🛠️ Refactor suggestion

Button import inconsistency needs to be resolved

You've imported Button from @tryghost/shade but also kept Button as ButtonX from the design system. Per previous reviewer comments, you should fully migrate to using the Shade button component throughout the file.

import {Button, LucideIcon, Skeleton} from '@tryghost/shade';
-import {Button as ButtonX, Heading, Icon, showToast} from '@tryghost/admin-x-design-system';
+import {Heading, Icon, showToast} from '@tryghost/admin-x-design-system';
🧹 Nitpick comments (2)
apps/admin-x-activitypub/src/components/feed/FeedItem.tsx (2)

228-238: Verify if the comment block should be removed

There's a commented block of code explaining menu item creation for delete functionality. Since you've now implemented this functionality in the FeedItemMenu component, consider if this comment should be removed.

-    // TODO: If this is your own Note/Article, you should be able to delete it
-    // menuItems.push({
-    //     id: 'delete',
-    //     label: 'Delete',
-    //     destructive: true,
-    //     onClick: handleDelete
-    // });

259-261: Potential UI improvement for interactive elements

You've adjusted the flex layout here. For better accessibility, consider adding a hover state for interactive elements within this container to provide visual feedback to users.

-<div className='flex min-w-0 items-center gap-3'>
+<div className='flex min-w-0 items-center gap-3 group'>
 <APAvatar author={author} />
-<div className='flex min-w-0 grow flex-col gap-0.5'>
+<div className='flex min-w-0 grow flex-col gap-0.5 group-hover:opacity-90 transition-opacity'>
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between dad596d and 8fc055e.

📒 Files selected for processing (1)
  • apps/admin-x-activitypub/src/components/feed/FeedItem.tsx (8 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
apps/admin-x-activitypub/src/components/feed/FeedItem.tsx

[error] 418-418: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

🔇 Additional comments (6)
apps/admin-x-activitypub/src/components/feed/FeedItem.tsx (6)

1-1: Good addition of the FeedItemMenu component

The component import supports the PR objective of adding delete functionality to ActivityPub feed items.


241-243: Good use of Shade Button component for the menu trigger

The menu trigger now uses the Shade Button component with appropriate styling and icon, which is consistent with the PR objective of replacing components with ones from Shade.


279-283: Well-structured implementation of FeedItemMenu

The FeedItemMenu component nicely encapsulates the menu functionality, receiving the trigger element and callback functions as props. This is a cleaner approach than the previous implementation with menuItems array.


371-375: Consistent implementation of FeedItemMenu across different layouts

The FeedItemMenu is consistently implemented in the modal layout, which is good for maintainability.


408-412: Consistent implementation in reply layout

The FeedItemMenu is correctly implemented in the reply layout as well.


503-507: Consistent implementation in inbox layout

The FeedItemMenu is correctly implemented in the inbox layout as well, maintaining consistency across all UI variations.

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 (4)
apps/admin-x-activitypub/src/components/feed/FeedItemMenu.tsx (4)

20-24: Consider implementing the children pattern as suggested in previous feedback.

Based on the previous review comment from peterzimon, consider using React children instead of a trigger prop. This would follow a more common React pattern and make your component usage more intuitive.

You could restructure the interface and component like this:

interface FeedItemMenuProps {
-    trigger: React.ReactNode;
    onCopyLink: () => void;
    onDelete: () => void;
+    children: React.ReactNode;
}

-const FeedItemMenu: React.FC<FeedItemMenuProps> = ({
-    trigger,
-    onCopyLink,
-    onDelete
-}) => {
+const FeedItemMenu: React.FC<FeedItemMenuProps> = ({
+    children,
+    onCopyLink,
+    onDelete
+}) => {

And then in the JSX:

<PopoverTrigger asChild onClick={e => e.stopPropagation()}>
-    {trigger}
+    {children}
</PopoverTrigger>

This would allow more intuitive usage:

<FeedItemMenu onCopyLink={...} onDelete={...}>
   <Button>Open menu</Button>
</FeedItemMenu>

73-78: Consider revising the confirmation message tone.

The current message "You're about to delete this post. This is permanent! We warned you, k?" has a casual tone with "k?" that might not match the professional nature of an admin interface.

<AlertDialogTitle>Are you sure you want to delete this post?</AlertDialogTitle>
<AlertDialogDescription>
-    You&apos;re about to delete this post. This is permanent! We warned you, k?
+    This action cannot be undone. The post will be permanently removed from your ActivityPub feed.
</AlertDialogDescription>

33-41: Add error handling for callback operations.

The component currently doesn't handle any potential errors that might occur during the copy link or delete operations.

Consider adding try/catch blocks to handle potential errors gracefully:

const handleCopyLinkClick = (e: React.MouseEvent<HTMLElement>) => {
    e.stopPropagation();
-    onCopyLink();
+    try {
+        onCopyLink();
+    } catch (error) {
+        console.error('Failed to copy link:', error);
+        // Optionally show an error notification to the user
+    }
};

const handleDeleteClick = (e: React.MouseEvent<HTMLElement>) => {
    e.stopPropagation();
-    onDelete();
+    try {
+        onDelete();
+    } catch (error) {
+        console.error('Failed to delete post:', error);
+        // Optionally show an error notification to the user
+    }
};

1-17: Consider grouping related imports for better organization.

The import statement is quite lengthy. Consider grouping the imports by their related functionality for better readability.

import {
+    // Alert Dialog components
    AlertDialog,
    AlertDialogAction,
    AlertDialogCancel,
    AlertDialogContent,
    AlertDialogDescription,
    AlertDialogFooter,
    AlertDialogHeader,
    AlertDialogTitle,
    AlertDialogTrigger,
+    // Button components
    Button,
+    // Popover components
    Popover,
    PopoverClose,
    PopoverContent,
    PopoverTrigger,
    buttonVariants
} from '@tryghost/shade';

Or alternatively, structure them by semantic groups:

import {
+    // UI Components
    AlertDialog,
    Button,
    Popover,
+    // Subcomponents
    AlertDialogAction,
    AlertDialogCancel,
    AlertDialogContent,
    AlertDialogDescription,
    AlertDialogFooter,
    AlertDialogHeader,
    AlertDialogTitle,
    AlertDialogTrigger,
    PopoverClose,
    PopoverContent,
    PopoverTrigger,
+    // Utilities
    buttonVariants
} from '@tryghost/shade';
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 8fc055e and 6da42a9.

📒 Files selected for processing (1)
  • apps/admin-x-activitypub/src/components/feed/FeedItemMenu.tsx (1 hunks)
🔇 Additional comments (4)
apps/admin-x-activitypub/src/components/feed/FeedItemMenu.tsx (4)

46-48: LGTM! Smart implementation to prevent event bubbling.

The stopPropagation() call here is important to prevent the click event from triggering actions on parent elements.


56-68: Well-implemented feature flag for the delete button.

Good practice using a feature flag to control the availability of the delete functionality. This allows for gradual rollout and A/B testing.


60-65: Excellent use of color semantics for the destructive action.

The delete button styling appropriately uses red for the text and hover state, clearly indicating it's a destructive action.


83-88: Good implementation of a destructive variant for the confirm button.

Using the buttonVariants utility with the destructive variant is a good practice for styling confirmation buttons for destructive actions.

@minimaluminium minimaluminium force-pushed the ap-feed-item-menu-AP-819 branch from 6da42a9 to 139a638 Compare March 5, 2025 03:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants