-
-
Notifications
You must be signed in to change notification settings - Fork 10.6k
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 post delete mutation for posts in the admin-x-activitypub
app
#22330
Added post delete mutation for posts in the admin-x-activitypub
app
#22330
Conversation
WalkthroughThe changes introduce a new exported interface Possibly related PRs
Suggested labels
Suggested reviewers
📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (1)
✅ Files skipped from review due to trivial changes (1)
⏰ Context from checks skipped due to timeout of 90000ms (4)
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
a4716b4
to
11cd1a5
Compare
There was a problem hiding this 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 (3)
apps/admin-x-activitypub/src/hooks/use-activity-pub-queries.ts (1)
819-882
: Comprehensive implementation with room for improvement in cache managementThe implementation is solid, handling optimistic updates and error recovery for feed and inbox. However, despite defining query keys for
outboxQueryKey
,likedQueryKey
, andprofilePostsQueryKey
, the code doesn't update these caches optimistically, which could lead to stale data.Consider updating all defined caches to maintain consistent state across the application. Here's a suggestion for extending the optimistic updates:
onMutate: (id) => { // Find the post in the feed query cache const previousFeed = queryClient.getQueryData<{pages: {posts: Activity[]}[]}[]>(feedQueryKey); queryClient.setQueryData(feedQueryKey, (current?: {pages: {posts: Activity[]}[]}) => { if (!current) { return current; } return { ...current, pages: current.pages.map((page: {posts: Activity[]}) => { return { ...page, posts: page.posts.filter((item: Activity) => item.id !== id) }; }) }; }); // Find the post in the inbox query cache const previousInbox = queryClient.getQueryData<{pages: {posts: Activity[]}[]}[]>(inboxQueryKey); queryClient.setQueryData(inboxQueryKey, (current?: {pages: {posts: Activity[]}[]}) => { if (!current) { return current; } return { ...current, pages: current.pages.map((page: {posts: Activity[]}) => { return { ...page, posts: page.posts.filter((item: Activity) => item.id !== id) }; }) }; }); + // Also update outbox, liked, and profile posts caches + const previousOutbox = queryClient.getQueryData<{pages: {data: Activity[]}[]}[]>(outboxQueryKey); + queryClient.setQueryData(outboxQueryKey, (current?: {pages: {data: Activity[]}[]}) => { + if (!current) { + return current; + } + + return { + ...current, + pages: current.pages.map((page: {data: Activity[]}) => { + return { + ...page, + data: page.data.filter((item: Activity) => item.id !== id) + }; + }) + }; + }); + + // Similar updates for likedQueryKey and profilePostsQueryKey + return { previousFeed, previousInbox, + previousOutbox, + // Add other previous states }; }, onError: (_err, _variables, context) => { queryClient.setQueryData(feedQueryKey, context?.previousFeed); queryClient.setQueryData(inboxQueryKey, context?.previousInbox); + queryClient.setQueryData(outboxQueryKey, context?.previousOutbox); + // Restore other caches }apps/admin-x-activitypub/src/components/feed/FeedItem.tsx (2)
218-224
: Improve user confirmation UX for delete operationThe current confirmation dialog is minimal and only shows the post ID, which isn't user-friendly. Consider showing a more descriptive message that includes some content from the post.
const handleDelete = (postId: string) => { - const confirm = window.confirm(`Delete post\n\n${postId}\n\n?`); + // Get a readable post identifier (title or first few characters of content) + const postIdentifier = object.name || + (object.content ? stripHtml(object.content).substring(0, 50) + '...' : 'this post'); + const confirm = window.confirm(`Are you sure you want to delete "${postIdentifier}"?\n\nThis action cannot be undone.`); if (confirm) { deletePostMutation.mutate(postId); } };
259-265
: Remove TODO commentThe TODO comment about deleting posts can now be removed since you've implemented the functionality.
- // 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 - // });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
apps/admin-x-activitypub/src/api/activitypub.ts
(4 hunks)apps/admin-x-activitypub/src/components/feed/FeedItem.tsx
(4 hunks)apps/admin-x-activitypub/src/hooks/use-activity-pub-queries.ts
(1 hunks)apps/admin-x-activitypub/src/utils/posts.ts
(1 hunks)
🔇 Additional comments (7)
apps/admin-x-activitypub/src/utils/posts.ts (1)
107-107
: LGTM! Appropriate handling of author status.The explicit comparison
=== true
ensures that the property is boolean, avoiding potential issues with falsy values. This change properly supports the post deletion feature being implemented.apps/admin-x-activitypub/src/components/feed/FeedItem.tsx (2)
195-195
: LGTM! Good initialization of the delete mutation hook.The hook is properly initialized with 'index' as the handle parameter.
251-257
: LGTM! Appropriate conditional rendering of delete optionThe delete menu item is correctly displayed only when the post is authored by the current user, ensuring proper access control.
apps/admin-x-activitypub/src/api/activitypub.ts (4)
104-104
: LGTM! Clear interface extension.Adding the
authoredByMe
boolean property to thePost
interface is a clean way to track post ownership.
137-137
: Type update correctly expands HTTP method supportThe update to include 'DELETE' as a valid method type is appropriate and follows TypeScript best practices.
151-155
: Proper handling of HTTP 204 No Content responsesThe implementation correctly handles 204 status code responses by returning null, which is appropriate for successful deletion operations that don't return content.
307-310
: Well-implemented delete methodThe delete method follows the established pattern in the class, using the appropriate URL structure and HTTP method. The implementation is clean and consistent with the codebase's style.
const previousFeed = queryClient.getQueryData<{pages: {posts: Activity[]}[]}[]>(feedQueryKey); | ||
queryClient.setQueryData(feedQueryKey, (current?: {pages: {posts: Activity[]}[]}) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Fix type assertion in getQueryData
The type assertion <{pages: {posts: Activity[]}[]}[]>
doesn't match the structure returned by the query. This could lead to runtime type errors.
- const previousFeed = queryClient.getQueryData<{pages: {posts: Activity[]}[]}[]>(feedQueryKey);
+ const previousFeed = queryClient.getQueryData<{pages: {posts: Activity[]}[]}>(feedQueryKey);
📝 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.
const previousFeed = queryClient.getQueryData<{pages: {posts: Activity[]}[]}[]>(feedQueryKey); | |
queryClient.setQueryData(feedQueryKey, (current?: {pages: {posts: Activity[]}[]}) => { | |
const previousFeed = queryClient.getQueryData<{pages: {posts: Activity[]}[]}>(feedQueryKey); | |
queryClient.setQueryData(feedQueryKey, (current?: {pages: {posts: Activity[]}[]}) => { |
const previousInbox = queryClient.getQueryData<{pages: {posts: Activity[]}[]}[]>(inboxQueryKey); | ||
queryClient.setQueryData(inboxQueryKey, (current?: {pages: {posts: Activity[]}[]}) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Fix type assertion in getQueryData
Same issue as previous comment - the type assertion doesn't match the structure returned by the query.
- const previousInbox = queryClient.getQueryData<{pages: {posts: Activity[]}[]}[]>(inboxQueryKey);
+ const previousInbox = queryClient.getQueryData<{pages: {posts: Activity[]}[]}>(inboxQueryKey);
📝 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.
const previousInbox = queryClient.getQueryData<{pages: {posts: Activity[]}[]}[]>(inboxQueryKey); | |
queryClient.setQueryData(inboxQueryKey, (current?: {pages: {posts: Activity[]}[]}) => { | |
const previousInbox = queryClient.getQueryData<{pages: {posts: Activity[]}[]}>(inboxQueryKey); | |
queryClient.setQueryData(inboxQueryKey, (current?: {pages: {posts: Activity[]}[]}) => { |
7f85b92
to
b36ce55
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (2)
apps/admin-x-activitypub/src/hooks/use-activity-pub-queries.ts (2)
837-837
:⚠️ Potential issueFix type assertion in getQueryData
The type assertion
<{pages: {posts: Activity[]}[]}[]>
doesn't match the expected structure returned by the query.- const previousFeed = queryClient.getQueryData<{pages: {posts: Activity[]}[]}[]>(QUERY_KEYS.feed); + const previousFeed = queryClient.getQueryData<{pages: {posts: Activity[]}[]}>(QUERY_KEYS.feed);
856-856
:⚠️ Potential issueFix type assertion in getQueryData for inbox
The type assertion has the same issue as the previous one.
- const previousInbox = queryClient.getQueryData<{pages: {posts: Activity[]}[]}[]>(QUERY_KEYS.inbox); + const previousInbox = queryClient.getQueryData<{pages: {posts: Activity[]}[]}>(QUERY_KEYS.inbox);
🧹 Nitpick comments (2)
apps/admin-x-activitypub/src/components/feed/FeedItem.tsx (2)
218-225
: Replace window.confirm with a proper modal for better UX.The confirmation currently uses a basic
window.confirm()
dialog, which doesn't align with the design system used throughout the application. This is correctly identified by your TODO comment.Consider implementing a modal dialog using the design system's components for a more consistent user experience.
const handleDelete = (postId: string) => { - // @TODO: Show confirmation modal - const confirm = window.confirm(`Delete post\n\n${postId}\n\n?`); - - if (confirm) { + // Use a design-system modal component instead of window.confirm + showModal({ + title: 'Delete post', + description: `Are you sure you want to delete this post?`, + confirmLabel: 'Delete', + cancelLabel: 'Cancel', + destructive: true, + onConfirm: () => { deleteMutation.mutate(postId); - } + } + }); };
260-266
: Remove commented-out code that's now implemented.The commented-out code is now obsolete since you've implemented the delete functionality. It should be removed to maintain code cleanliness.
-// 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 -// });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
apps/admin-x-activitypub/src/api/activitypub.ts
(4 hunks)apps/admin-x-activitypub/src/components/feed/FeedItem.tsx
(4 hunks)apps/admin-x-activitypub/src/hooks/use-activity-pub-queries.ts
(2 hunks)apps/admin-x-activitypub/src/utils/posts.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/admin-x-activitypub/src/utils/posts.ts
- apps/admin-x-activitypub/src/api/activitypub.ts
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: Setup
🔇 Additional comments (5)
apps/admin-x-activitypub/src/components/feed/FeedItem.tsx (3)
15-15
: Appropriate import for the new delete functionality.Good job adding the import for the delete mutation hook which will be used to implement post deletion.
195-195
: The deletion capability is properly initialized.Creating the mutation with the 'index' handle parameter is appropriate. This allows the component to perform deletion operations against the ActivityPub API.
252-258
: Conditional menu item correctly checks post ownership.The delete menu item is appropriately shown only for posts authored by the current user, which ensures users can only delete their own content.
apps/admin-x-activitypub/src/hooks/use-activity-pub-queries.ts (2)
67-73
: Thread query key properly handles null ID case.Good modification to handle the case when a thread ID is null. This allows for more flexible thread querying operations, which will be useful when managing thread-related operations.
825-913
: Well-implemented deletion mutation with optimistic updatesThe implementation follows best practices for React Query mutations:
- It provides optimistic updates to immediately reflect the deletion in the UI
- It handles all relevant caches (feed, inbox, thread)
- It implements proper error handling with cache restoration
This creates a smooth user experience while maintaining data consistency.
However, consider adding a success callback parameter to allow components to show success notifications:
export function useDeleteMutationForUser(handle: string) { const queryClient = useQueryClient(); return useMutation({ async mutationFn(id: string) { const siteUrl = await getSiteUrl(); const api = createActivityPubAPI(handle, siteUrl); return api.delete(id); }, + onSuccess: () => { + // Show success toast or trigger callback if provided + showToast({ + title: 'Post deleted successfully', + type: 'success' + }); + }, onMutate: (id) => { // ... existing optimistic update code }, onError: (_err, _variables, context) => { if (!context) { return; } queryClient.setQueryData(context.previousFeed.key, context.previousFeed.data); queryClient.setQueryData(context.previousInbox.key, context.previousInbox.data); queryClient.setQueryData(context.previousThreads.key, context.previousThreads.data); + + // Show error toast + showToast({ + title: 'Failed to delete post', + type: 'error' + }); } }); }
const threadQueryKey = QUERY_KEYS.thread(null); | ||
const previousThreads = queryClient.getQueryData<{posts: Activity[]}>(threadQueryKey); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Thread cache update should be more specific
Using a null ID for the thread query key is too broad - it might affect all thread queries rather than just the specific thread containing the deleted post. Consider using specific thread IDs when possible.
🏁 Script executed:
#!/bin/bash
# Check how the thread query key is used in the codebase
rg -A 5 "QUERY_KEYS.thread"
Length of output: 1278
Thread cache update should target a specific thread
The current implementation uses QUERY_KEYS.thread(null)
, which creates an overly broad cache key and may inadvertently update all thread queries rather than just the thread associated with the deleted post. For consistency and to prevent unexpected cache invalidation, please update this usage to use the appropriate thread ID (as seen elsewhere with QUERY_KEYS.thread(id)
).
- Update the cache key to use the specific thread ID instead of
null
in this file (apps/admin-x-activitypub/src/hooks/use-activity-pub-queries.ts, lines 875-876).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Post can appear in multiple threads which is why we can't target a single thread here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mike182uk Thank you for clarifying that approach. I understand now - since posts can appear in multiple threads, using QUERY_KEYS.thread(null)
is actually the correct strategy here as it allows you to update all thread caches that might contain the deleted post rather than targeting just a single thread.
This is a good implementation choice given the requirements. I'll note this information for future reviews.
✏️ Learnings added
Learnt from: mike182uk
PR: TryGhost/Ghost#22330
File: apps/admin-x-activitypub/src/hooks/use-activity-pub-queries.ts:911-912
Timestamp: 2025-03-04T17:19:50.786Z
Learning: In the ActivityPub app, posts can appear in multiple threads, so when updating thread caches (e.g., when deleting a post), using QUERY_KEYS.thread(null) is the appropriate approach to update all thread caches rather than targeting a specific thread.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
b36ce55
to
2c34fad
Compare
There was a problem hiding this 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 (3)
apps/admin-x-activitypub/src/hooks/use-activity-pub-queries.ts (1)
933-934
: Profile posts cache update is too broadSimilar to the thread query issue, using
QUERY_KEYS.profilePosts(null)
creates a broad cache key that may affect all profile posts queries rather than just those for the specific user. Consider using the handle of the user performing the deletion to target only their profile posts.- const profilePostsQueryKey = QUERY_KEYS.profilePosts(null); + const profilePostsQueryKey = QUERY_KEYS.profilePosts(handle);apps/admin-x-activitypub/src/components/feed/FeedItem.tsx (2)
232-239
: Replace window.confirm with a proper modal dialogThe TODO comment indicates the intention to use a confirmation modal, but the current implementation uses
window.confirm
, which doesn't match the design system of the application.Consider implementing a proper confirmation dialog using the application's design system components for a consistent user experience. For example, using a modal component from
@tryghost/admin-x-design-system
would be more consistent with the rest of the application.
274-280
: Remove commented-out codeThe commented-out code for the delete menu item is now redundant since you've implemented the functionality above. It should be removed to keep the codebase clean.
- // 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 - // });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
apps/admin-x-activitypub/src/api/activitypub.ts
(4 hunks)apps/admin-x-activitypub/src/components/feed/ArticleModal.tsx
(3 hunks)apps/admin-x-activitypub/src/components/feed/FeedItem.tsx
(6 hunks)apps/admin-x-activitypub/src/components/modals/ViewProfileModal.tsx
(1 hunks)apps/admin-x-activitypub/src/hooks/use-activity-pub-queries.ts
(3 hunks)apps/admin-x-activitypub/src/utils/posts.ts
(1 hunks)apps/admin-x-activitypub/src/views/Inbox/Inbox.tsx
(1 hunks)apps/admin-x-activitypub/src/views/Profile/Profile.tsx
(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/admin-x-activitypub/src/utils/posts.ts
⏰ Context from checks skipped due to timeout of 90000ms (4)
- GitHub Check: Unit tests (Node 22.13.1)
- GitHub Check: Unit tests (Node 20.11.1)
- GitHub Check: Unit tests (Node 18.12.1)
- GitHub Check: Lint
🔇 Additional comments (20)
apps/admin-x-activitypub/src/components/modals/ViewProfileModal.tsx (1)
172-172
: Added post deletion capability based on authorship.The new
allowDelete
prop appropriately controls whether deletion functionality is available based on the post's authorship status. This ensures users can only delete posts they've authored.apps/admin-x-activitypub/src/views/Inbox/Inbox.tsx (1)
93-93
: Deletion permissions properly restricted to post authors.The
allowDelete
prop implementation correctly restricts deletion functionality to posts authored by the current user, maintaining proper authorization boundaries.apps/admin-x-activitypub/src/components/feed/ArticleModal.tsx (3)
861-862
: Appropriate disabling of delete capability for thread parents.Setting
allowDelete
tofalse
for thread parents is correct since users shouldn't be able to delete parent posts in a thread that they didn't author. This maintains thread integrity.
882-883
: Comprehensive permission check for post deletion.The implementation correctly considers both authorship (
object.authored
) and thread context (threadParents.length === 0
). This ensures users can only delete their own posts and only when they aren't replies in a thread, which prevents orphaned replies.
950-951
: Thread reply deletion permissions properly implemented.Setting
allowDelete
based onitem.object.authored
ensures users can only delete their own replies in a thread, maintaining proper authorization boundaries while allowing for normal thread management.apps/admin-x-activitypub/src/api/activitypub.ts (4)
104-104
: Implementation ofauthoredByMe
property is correctAdding the
authoredByMe
boolean to thePost
interface will help determine whether the current user has permission to delete a post.
137-137
: Method signature correctly updated to include DELETE methodThe
fetchJSON
method signature has been properly updated to includeDELETE
alongside the existingGET
andPOST
HTTP methods.
152-154
: Good handling of 204 No Content responsesReturning
null
for 204 (No Content) responses is appropriate, as these responses don't contain a response body to parse as JSON.
307-310
: Implementation of delete method follows established patternsThe
delete
method implementation is concise and follows the same pattern as other API methods in this class, ensuring consistency in the codebase.apps/admin-x-activitypub/src/hooks/use-activity-pub-queries.ts (6)
50-55
: Query key structure for handling null parameters is appropriateThe modification to
profilePosts
query key function to handlenull
parameters is well implemented, supporting the cache update needs in the delete mutation.
72-77
: Query key structure for handling null parameters is appropriateSimilar to the
profilePosts
modification, thethread
query key function has been updated to handlenull
parameters correctly.
841-841
: Fix type assertion in getQueryDataThe type assertion
<{pages: {posts: Activity[]}[]}[]>
doesn't match the structure returned by the query. This could lead to runtime type errors.- const previousFeed = queryClient.getQueryData<{pages: {posts: Activity[]}[]}[]>(QUERY_KEYS.feed); + const previousFeed = queryClient.getQueryData<{pages: {posts: Activity[]}[]}>(QUERY_KEYS.feed);
860-860
: Fix type assertion in getQueryDataSame issue as previous comment - the type assertion doesn't match the structure returned by the query.
- const previousInbox = queryClient.getQueryData<{pages: {posts: Activity[]}[]}[]>(QUERY_KEYS.inbox); + const previousInbox = queryClient.getQueryData<{pages: {posts: Activity[]}[]}>(QUERY_KEYS.inbox);
879-880
: Thread cache update should target a specific threadThe current implementation uses
QUERY_KEYS.thread(null)
, which creates an overly broad cache key and may inadvertently update all thread queries rather than just the thread associated with the deleted post. For consistency and to prevent unexpected cache invalidation, please update this usage to use the appropriate thread ID (as seen elsewhere withQUERY_KEYS.thread(id)
).
829-992
: Well implemented delete mutation function with optimistic updatesThe
useDeleteMutationForUser
function is well structured, following the established pattern in the codebase for mutation functions. It properly:
- Initializes the mutation function with the appropriate API call
- Implements optimistic updates for all relevant caches
- Provides context for error handling and rollback
- Returns proper types
Despite the minor issues noted in other comments, this implementation is solid and will handle deletion workflow effectively.
apps/admin-x-activitypub/src/components/feed/FeedItem.tsx (5)
15-15
: Import for delete mutation hook is appropriately addedThe import for
useDeleteMutationForUser
is correctly added to support the new deletion functionality.
172-172
: New prop for deletion permissions is well designedThe optional
allowDelete
property in theFeedItemProps
interface is a good approach for conditionally enabling delete functionality based on post ownership.
187-200
: Component parameter list with default value is correctly implementedThe component signature is properly updated to include the new
allowDelete
prop with a sensible default value offalse
.
209-209
: Delete mutation hook is properly initializedThe
deleteMutation
is correctly initialized using theuseDeleteMutationForUser
hook with the 'index' handle.
266-272
: Delete menu item implementation is well integratedThe conditional rendering of the delete menu item based on the
allowDelete
prop is well implemented and integrates smoothly with the existing menu structure.
allowDelete={true} // TODO: Only true if the user is the author | ||
layout='feed' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
TODO comment identifies a permission inconsistency.
The TODO comment correctly identifies that the current implementation doesn't properly restrict deletion permissions for liked content. Users should only be able to delete content they authored, not all content they've liked.
Implement the condition as suggested in the TODO:
-allowDelete={true} // TODO: Only true if the user is the author
+allowDelete={activity.object?.authoredByMe === true}
📝 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.
allowDelete={true} // TODO: Only true if the user is the author | |
layout='feed' | |
allowDelete={activity.object?.authoredByMe === true} | |
layout='feed' |
@@ -146,6 +146,7 @@ | |||
> | |||
<FeedItem | |||
actor={activity.actor} | |||
allowDelete={true} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Unconditional deletion permission requires validation.
Setting allowDelete
to true
unconditionally for posts in the user's profile might be appropriate since users should be able to delete their own posts. However, this differs from the pattern in other components where deletion is conditionally based on the authored
property, which could lead to inconsistent behavior.
To ensure this approach is intentional, let's check if other components follow this pattern:
🏁 Script executed:
#!/bin/bash
# Find all places where allowDelete is set and check if there are inconsistencies in its usage
rg -A 5 -B 5 "allowDelete" --type tsx
Length of output: 65
Let's re-run the search with a query that correctly targets TSX files. The previous command failed because --type tsx
isn’t recognized by rg
. We can instead use the built-in type for TypeScript React files. Please verify by running the following shell script:
This should help confirm if other components conditionally set allowDelete
(perhaps based on the authored
property) or if the behavior in apps/admin-x-activitypub/src/views/Profile/Profile.tsx
is consistent with the rest of the codebase.
🏁 Script executed:
#!/bin/bash
rg -A 5 -B 5 "allowDelete" -t typescriptreact
Length of output: 85
Let's run an updated search script that targets TSX files using a filename glob pattern. This script will search for both allowDelete
and authored
patterns to check if other components use conditional deletion permission.
🏁 Script executed:
#!/bin/bash
echo "Searching for 'allowDelete' in TSX files:"
rg -A 5 -B 5 "allowDelete" -g "*.tsx"
echo -e "\nSearching for 'authored' in TSX files:"
rg -A 5 -B 5 "authored" -g "*.tsx"
Length of output: 17911
Subject: Inconsistent Deletion Permission in Profile.tsx
We found that the Profile view sets allowDelete
unconditionally to true
(line 149), whereas other components (like in Inbox, ArticleModal, and ViewProfileModal) conditionally enable deletion based on the post’s authored
property. There's even a TODO comment in Profile.tsx suggesting this needs review. Please verify if this design is intentional. If not, consider updating Profile.tsx to align with the conditional check—e.g., using activity.object.authored
—to ensure users can delete only their own posts.
751c53a
to
da2d8ae
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
apps/admin-x-activitypub/src/api/activitypub.ts
(5 hunks)apps/admin-x-activitypub/src/components/feed/ArticleModal.tsx
(8 hunks)apps/admin-x-activitypub/src/components/feed/FeedItem.tsx
(6 hunks)apps/admin-x-activitypub/src/components/modals/ViewProfileModal.tsx
(1 hunks)apps/admin-x-activitypub/src/hooks/use-activity-pub-queries.ts
(5 hunks)apps/admin-x-activitypub/src/utils/posts.ts
(1 hunks)apps/admin-x-activitypub/src/views/Inbox/Inbox.tsx
(1 hunks)apps/admin-x-activitypub/src/views/Profile/Profile.tsx
(2 hunks)apps/admin-x-activitypub/test/unit/utils/posts.test.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
- apps/admin-x-activitypub/src/components/modals/ViewProfileModal.tsx
- apps/admin-x-activitypub/src/utils/posts.ts
- apps/admin-x-activitypub/src/views/Inbox/Inbox.tsx
- apps/admin-x-activitypub/src/api/activitypub.ts
- apps/admin-x-activitypub/src/components/feed/ArticleModal.tsx
- apps/admin-x-activitypub/src/components/feed/FeedItem.tsx
- apps/admin-x-activitypub/src/views/Profile/Profile.tsx
🔇 Additional comments (16)
apps/admin-x-activitypub/test/unit/utils/posts.test.ts (1)
42-42
: Good addition of authoredByMe propertyThe addition of the
authoredByMe
property to the post test fixture is appropriate and aligns with the implementation of deletion functionality. This ensures the test properly validates the mapping of this property.apps/admin-x-activitypub/src/hooks/use-activity-pub-queries.ts (15)
2-2
: Good addition of Account type importThe Account type import is correctly added to support the new deletion functionality, as it's referenced in the implementation of the
useDeleteMutationForUser
function.
51-56
: Well-structured and type-safe query key handlingThe updated
profilePosts
function now properly handles null values, following a consistent pattern that returns an appropriate key array. This allows for targeting either specific profile posts or all profile posts collectively.
73-78
: Consistent query key pattern implementationThe thread query key handling follows the same pattern as the profilePosts function, correctly handling both null and string ID cases. This is good for code consistency and facilitates global cache invalidation.
650-652
: Good inclusion of authorship informationSetting the
authored
property ensures that newly created notes are correctly marked as being authored by the current user, which is essential for determining deletion permissions in the UI.
833-842
: Well-implemented mutation hook for post deletionThe
useDeleteMutationForUser
function is well-structured, accepting the user handle and creating a mutation that calls the appropriate API method. The mutation function correctly handles both post ID and optional parent ID parameters.
843-876
: Comprehensive feed cache updates for post deletionThe feed cache update logic thoroughly:
- Removes the deleted post from the feed
- Updates the parent post's reply count when appropriate
- Uses proper type narrowing with
!current
checksThis approach ensures consistent user experience after deletion.
877-910
: Inbox cache update follows consistent patternThe inbox cache update logic follows the same pattern as the feed update, maintaining code consistency while providing proper optimistic updates for the inbox.
911-938
: Thread cache invalidation is properly implementedThe thread cache update correctly:
- Uses the global thread query key (
null
)- Properly removes the deleted post from all relevant threads
- Updates parent posts' reply counts
This follows the established pattern to ensure all instances of a post are removed from cached thread data.
915-916
: Thread cache update targets all thread data appropriatelyUsing
QUERY_KEYS.thread(null)
is the correct approach since a post can appear in multiple threads. This allows the operation to update all thread caches rather than targeting a specific thread.
939-959
: Well-handled outbox cache updateThe outbox cache update properly:
- Uses the correct query key based on the user handle
- Filters out the deleted post from each page of data
- Maintains the overall structure of the outbox data
This ensures that the user's outbox is immediately updated after post deletion.
960-983
: Smart approach for liked posts cache updateThe liked posts cache update includes tracking whether the post was removed from the liked collection, which is then used to determine if the account's liked count needs to be updated. This attention to detail ensures data consistency.
984-1004
: Comprehensive profile posts cache updateThe profile posts cache update uses the global query key to update all profile posts caches, ensuring that the deleted post is removed from all relevant profile views regardless of which profile they appear in.
1005-1025
: Conditional account cache update based on like statusThe account cache update is only performed if the post was removed from the liked collection, which is an efficient approach that avoids unnecessary updates. The like count is also properly bounded to avoid negative values.
1026-1057
: Well-structured context object for error handlingThe return object for the mutation context is well-organized, containing all previous cache states needed for restoration in case of error. The conditional inclusion of previous account data based on whether it was updated is a nice optimization.
1058-1073
: Robust error handling with complete cache restorationThe
onError
handler thoroughly restores all cache states to their previous values if the mutation fails, providing a seamless rollback. The null check for context and conditional restoration of account data show careful attention to edge cases.
refs [AP-806](https://linear.app/ghost/issue/AP-806/wire-up-the-delete-buttons-to-the-deleteid-api) Added post delete mutation for posts in the `admin-x-activitypub` app
da2d8ae
to
f59aa81
Compare
@@ -815,3 +829,247 @@ export function useInboxForUser(options: {enabled: boolean}) { | |||
|
|||
return {inboxQuery, updateInboxActivity}; | |||
} | |||
|
|||
export function useDeleteMutationForUser(handle: string) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Damn this query cache is painful 😢
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
apps/admin-x-activitypub/src/hooks/use-activity-pub-queries.ts (1)
833-1075
: Consider adding user-facing error messagingWhile the cache restoration on error is well-implemented, consider adding a mechanism to communicate deletion failures to the user.
export function useDeleteMutationForUser(handle: string) { const queryClient = useQueryClient(); + const toast = useToast(); return useMutation({ // ... existing code onError: (_err, _variables, context) => { if (!context) { return; } // Restore caches queryClient.setQueryData(context.previousFeed.key, context.previousFeed.data); // ... other cache restorations + // Notify user of the error + toast.error('Failed to delete post. Please try again.'); } }); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
apps/admin-x-activitypub/src/api/activitypub.ts
(5 hunks)apps/admin-x-activitypub/src/components/feed/ArticleModal.tsx
(8 hunks)apps/admin-x-activitypub/src/components/feed/FeedItem.tsx
(9 hunks)apps/admin-x-activitypub/src/components/feed/FeedItemMenu.tsx
(2 hunks)apps/admin-x-activitypub/src/components/modals/ViewProfileModal.tsx
(1 hunks)apps/admin-x-activitypub/src/hooks/use-activity-pub-queries.ts
(5 hunks)apps/admin-x-activitypub/src/utils/posts.ts
(1 hunks)apps/admin-x-activitypub/src/views/Inbox/Inbox.tsx
(1 hunks)apps/admin-x-activitypub/src/views/Profile/Profile.tsx
(2 hunks)apps/admin-x-activitypub/test/unit/utils/posts.test.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
- apps/admin-x-activitypub/src/utils/posts.ts
- apps/admin-x-activitypub/src/views/Profile/Profile.tsx
- apps/admin-x-activitypub/src/components/feed/ArticleModal.tsx
- apps/admin-x-activitypub/src/views/Inbox/Inbox.tsx
- apps/admin-x-activitypub/test/unit/utils/posts.test.ts
- apps/admin-x-activitypub/src/components/feed/FeedItem.tsx
- apps/admin-x-activitypub/src/components/modals/ViewProfileModal.tsx
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: Setup
🔇 Additional comments (18)
apps/admin-x-activitypub/src/components/feed/FeedItemMenu.tsx (3)
24-24
: Property added for conditional renderingThe addition of the
allowDelete
boolean property to the interface is well-defined and enables fine-grained control over the delete functionality visibility.
30-31
: Default value for allowDelete is set appropriatelyGood practice setting the default value to
false
- this ensures the component behaves predictably when the property is not provided.
58-70
: Delete button now controlled by dual conditionsThe conditional rendering now properly checks both the feature flag and the new
allowDelete
prop, ensuring the delete button only appears when both conditions are met. This implementation allows for more granular control based on post ownership.apps/admin-x-activitypub/src/api/activitypub.ts (5)
15-30
: New Account interface looks goodThe exported
Account
interface is well-structured with appropriate types for all properties.
104-104
: Post interface now tracks authorshipThe addition of the
authoredByMe
property to thePost
interface enables implementing permission-based UI controls like the delete button.
137-137
: API now supports DELETE methodThe
fetchJSON
method signature has been updated to accept theDELETE
HTTP method, enabling the post deletion functionality.
151-154
: Proper handling of 204 No Content responseCorrectly handles the HTTP 204 (No Content) response that's expected from successful deletion operations, returning
null
to indicate success without a response body.
307-310
: Delete method implementationThe new
delete
method is implemented consistently with other API methods, creating the appropriate URL and using theDELETE
HTTP method withfetchJSON
.apps/admin-x-activitypub/src/hooks/use-activity-pub-queries.ts (10)
51-56
: Query key function now handles null valuesThe
profilePosts
function has been updated to handlenull
values, which enables broader cache invalidation when needed. This approach is consistent with the implementation requirements.
73-78
: Thread query key function now handles null valuesSimilarly, the
thread
function has been updated to handlenull
values. Based on the previous comments, this enables invalidating all thread caches when a post that might appear in multiple threads is deleted.
650-652
: Ensures authored flag is set on new postsGood addition to set
authored = true
on newly created posts, providing consistency with the new authorship tracking property.
833-842
: Delete mutation implementationThe
useDeleteMutationForUser
hook is well-implemented, following the established patterns in the codebase. It takes a handle parameter and returns a mutation for deleting posts by ID.
847-847
: Fix type assertion in getQueryData callsThe type assertions in several getQueryData calls don't match the structure returned by the query, which could lead to runtime type errors.
843-1056
: Comprehensive optimistic updates for delete operationsThe
onMutate
callback thoroughly updates all necessary caches to maintain data consistency:
- Removes the deleted post from feed, inbox, threads, outbox, liked, and profile posts
- Decrements the reply count of parent posts when applicable
- Updates the liked post count in the account cache if necessary
Good job saving the previous cache state for potential rollback.
915-916
: Thread cache update targets all threadsUsing
QUERY_KEYS.thread(null)
is the correct approach since posts can appear in multiple threads, as confirmed in the past review comments.
987-988
: Comprehensive profile posts cleanupThe approach to invalidate all profile posts queries using the null key is efficient for ensuring that deleted posts are removed from all relevant profile views.
1011-1025
: Account liked count update respects stateGood implementation that only decrements the account's liked count if the post was actually removed from the liked collection, and includes a bounds check to prevent negative counts.
1058-1073
: Robust error handling with cache restorationThe error handling restores all affected caches to their previous state if the deletion fails, providing a good user experience by maintaining data consistency.
refs AP-806
Added post delete mutation for posts in the
admin-x-activitypub
app