Updated ActivityPub to use dynamic topics and recommendations from API#25646
Updated ActivityPub to use dynamic topics and recommendations from API#25646minimaluminium merged 11 commits intomainfrom
Conversation
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughBumps apps/activitypub package version from 2.0.5 to 3.0.0. Adds TopicData, GetTopicsResponse, GetRecommendationsResponse interfaces and getTopics/getRecommendations methods to ActivityPubAPI. Introduces a new useTopicsForUser hook and updates useSuggestedProfilesForUser to use getRecommendations and cache accounts. TopicFilter now sources topics from the API and widens Topic to string. Multiple UI components (Layout, Sidebar, InboxList, Search, SuggestedProfiles, Feed SuggestedProfiles, Onboarding Step3) are updated to be topic-aware or conditionally render based on topics. Several components stop showing/updating followerCount and SuggestedProfiles consolidates scroll-button logic. Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
apps/activitypub/src/components/TopicFilter.tsx (1)
14-22: Consider exposing loading state for better UX.When
topicsQueryis loading,apiTopicswill be an empty array, showing only the "Following" topic. This could cause a brief flash where users see fewer topics before the full list appears. Depending on the parent component's needs, consider:
- Exposing
isLoadingfrom the hook so parents can show a skeleton/placeholder- Or returning early with a loading indicator if the full topic list is important
If the current behavior (gracefully degrading to just "Following" during load) is intentional, this is fine as-is.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
apps/activitypub/package.json(1 hunks)apps/activitypub/src/api/activitypub.ts(2 hunks)apps/activitypub/src/components/TopicFilter.tsx(3 hunks)apps/activitypub/src/components/global/SuggestedProfiles.tsx(1 hunks)apps/activitypub/src/components/layout/Layout.tsx(3 hunks)apps/activitypub/src/components/layout/Sidebar/Sidebar.tsx(3 hunks)apps/activitypub/src/components/modals/Search.tsx(3 hunks)apps/activitypub/src/hooks/use-activity-pub-queries.ts(3 hunks)apps/activitypub/src/views/Feed/components/SuggestedProfiles.tsx(1 hunks)apps/activitypub/src/views/Inbox/components/InboxList.tsx(3 hunks)
🧰 Additional context used
🧠 Learnings (10)
📚 Learning: 2025-03-13T09:00:20.205Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/utils/pending-activity.ts:13-71
Timestamp: 2025-03-13T09:00:20.205Z
Learning: The pending activity utilities in the Ghost ActivityPub module are covered by tests in the file `apps/admin-x-activitypub/test/unit/utils/pending-activity.ts`.
Applied to files:
apps/activitypub/package.jsonapps/activitypub/src/components/modals/Search.tsx
📚 Learning: 2025-03-13T09:00:20.205Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/utils/pending-activity.ts:13-71
Timestamp: 2025-03-13T09:00:20.205Z
Learning: The pending activity utilities in Ghost's ActivityPub module are thoroughly tested in `apps/admin-x-activitypub/test/unit/utils/pending-activity.ts`, which covers `generatePendingActivityId`, `isPendingActivity`, and `generatePendingActivity` functions.
Applied to files:
apps/activitypub/package.jsonapps/activitypub/src/components/modals/Search.tsx
📚 Learning: 2025-03-13T09:02:50.102Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/views/Feed/components/NewPostModal.tsx:29-34
Timestamp: 2025-03-13T09:02:50.102Z
Learning: In the Ghost ActivityPub module, error handling for mutations is handled at the hook level (in use-activity-pub-queries.ts) rather than in individual components. This allows for centralized error handling across the application.
Applied to files:
apps/activitypub/package.jsonapps/activitypub/src/components/modals/Search.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/layout/**/*.{ts,tsx} : Reusable layout containers (Page, Heading, Header, ViewHeader, ErrorPage) should be placed in `src/components/layout/*`
Applied to files:
apps/activitypub/src/components/layout/Layout.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Prefer compound subcomponents (e.g., `Header.Title`, `Header.Meta`, `Header.Actions`) for multi-region components instead of many props
Applied to files:
apps/activitypub/src/components/layout/Layout.tsx
📚 Learning: 2025-11-25T14:28:50.351Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T14:28:50.351Z
Learning: Use React with Vite for Admin app development (admin-x-settings, admin-x-activitypub, posts, stats)
Applied to files:
apps/activitypub/src/components/layout/Layout.tsxapps/activitypub/src/components/modals/Search.tsx
📚 Learning: 2025-03-13T09:02:50.102Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/views/Feed/components/NewPostModal.tsx:29-34
Timestamp: 2025-03-13T09:02:50.102Z
Learning: In the ActivityPub module, error handling for mutations is handled at the hook level (in use-activity-pub-queries.ts) rather than in individual components. This allows for centralized error handling across the application.
Applied to files:
apps/activitypub/src/components/modals/Search.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/hooks/**/*.{ts,tsx} : Custom React hooks should be placed in `src/hooks/*`
Applied to files:
apps/activitypub/src/components/TopicFilter.tsx
📚 Learning: 2025-10-27T11:59:33.968Z
Learnt from: peterzimon
Repo: TryGhost/Ghost PR: 25261
File: apps/admin/src/layout/app-sidebar/UserMenu.tsx:24-29
Timestamp: 2025-10-27T11:59:33.968Z
Learning: In apps/admin/src/layout/app-sidebar/UserMenu.tsx, the hardcoded placeholder data (avatar URL, initials "US", name "User Name", email "userexample.com") is a known limitation and should not be flagged for replacement with real user data.
Applied to files:
apps/activitypub/src/components/layout/Sidebar/Sidebar.tsx
📚 Learning: 2025-11-05T16:49:21.124Z
Learnt from: rob-ghost
Repo: TryGhost/Ghost PR: 25356
File: apps/admin/src/hooks/user-preferences.ts:20-20
Timestamp: 2025-11-05T16:49:21.124Z
Learning: In apps/admin/src/hooks/user-preferences.ts, the query key for user preferences intentionally includes `user?.accessibility` (not just `user?.id`) so the cache automatically reacts to changes from any source (useEditUserPreferences, other editUser calls, external updates). Combined with `cacheTime: 0`, this design garbage-collects orphaned entries while keeping the active entry cached. This is a documented, intentional architectural decision.
Applied to files:
apps/activitypub/src/hooks/use-activity-pub-queries.ts
🧬 Code graph analysis (6)
apps/activitypub/src/views/Inbox/components/InboxList.tsx (1)
apps/activitypub/src/hooks/use-activity-pub-queries.ts (1)
useTopicsForUser(2722-2735)
apps/activitypub/src/components/layout/Layout.tsx (1)
apps/activitypub/src/hooks/use-activity-pub-queries.ts (1)
useTopicsForUser(2722-2735)
apps/activitypub/src/components/modals/Search.tsx (1)
apps/activitypub/src/hooks/use-activity-pub-queries.ts (1)
useSuggestedProfilesForUser(2677-2720)
apps/activitypub/src/components/TopicFilter.tsx (1)
apps/activitypub/src/hooks/use-activity-pub-queries.ts (1)
useTopicsForUser(2722-2735)
apps/activitypub/src/components/layout/Sidebar/Sidebar.tsx (2)
apps/activitypub/src/hooks/use-activity-pub-queries.ts (1)
useTopicsForUser(2722-2735)ghost/core/core/server/services/koenig/node-renderers/image-renderer.js (1)
a(114-114)
apps/activitypub/src/hooks/use-activity-pub-queries.ts (1)
apps/activitypub/src/api/activitypub.ts (1)
Account(9-31)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Cursor Bugbot
- GitHub Check: Setup
- GitHub Check: Build & Push
- GitHub Check: Setup
🔇 Additional comments (16)
apps/activitypub/package.json (1)
3-3: LGTM: Version bump is appropriate.The patch version increment aligns with the new features and API additions introduced in this PR.
apps/activitypub/src/api/activitypub.ts (2)
43-54: LGTM: Well-defined interfaces.The new TypeScript interfaces for topics and recommendations are clean and follow the existing patterns in the codebase.
492-509: Backend endpoints are not implemented.The
getTopics()andgetRecommendations()methods call.ghost/activitypub/v1/topicsand.ghost/activitypub/v1/recommendationsendpoints, but these endpoints do not exist in the backend. Without corresponding server-side route handlers, these methods will fail at runtime. Either implement the missing endpoints or remove these unused methods.⛔ Skipped due to learnings
Learnt from: CR Repo: TryGhost/Ghost PR: 0 File: AGENTS.md:0-0 Timestamp: 2025-11-25T14:28:50.351Z Learning: API routes should be defined in `ghost/core/core/server/api/`apps/activitypub/src/components/global/SuggestedProfiles.tsx (1)
84-86: LGTM: Good defensive rendering.The early return prevents rendering empty UI sections when there are no suggested profiles to display.
apps/activitypub/src/views/Inbox/components/InboxList.tsx (2)
11-11: LGTM: Proper integration of topics data.The hook usage follows the established pattern and provides a safe fallback when the topics query fails (hasTopics will be false if topicsData is undefined).
Also applies to: 36-38
79-79: LGTM: Conditional topic filter rendering.The TopicFilter is appropriately hidden when no topics are available, preventing an empty or broken UI state.
apps/activitypub/src/views/Feed/components/SuggestedProfiles.tsx (1)
19-30: LGTM: Deduplication of scroll button logic.Moving
updateScrollButtonsto the top eliminates the duplicate definition and makes it accessible to all consumers within the component.apps/activitypub/src/components/layout/Layout.tsx (1)
11-11: LGTM: Topic-aware border rendering.The layout now conditionally adjusts the header border based on topic availability for the reader page, maintaining visual consistency with the topic filter's presence.
Also applies to: 20-22, 51-51
apps/activitypub/src/components/modals/Search.tsx (2)
9-9: LGTM: Integration of suggested profiles hook.The hook usage is clean and the derived
hasSuggestedProfilesstate properly accounts for both loading and data presence.Also applies to: 130-132
190-190: LGTM: Conditional rendering prevents empty sections.The combined condition ensures the "More people to follow" section only appears when appropriate and when suggestions are actually available.
apps/activitypub/src/components/layout/Sidebar/Sidebar.tsx (2)
13-13: LGTM: Topic-aware sidebar rendering.The hook integration follows the established pattern and provides the foundation for conditional Explore link behavior.
Also applies to: 29-31
79-96: LGTM: Conditional Explore link with external fallback.When topics are unavailable, the external link to Ghost's discovery page provides users with an alternative way to find content. The link properly uses
rel="noopener noreferrer"and includes an external link icon for clarity.apps/activitypub/src/components/TopicFilter.tsx (1)
49-58: LGTM!The rendering logic correctly uses
slugfor keys and comparisons, and displays the human-readablename. This aligns well with the new dynamic topics data structure.apps/activitypub/src/hooks/use-activity-pub-queries.ts (3)
95-97: LGTM!The
topicsquery key follows the established pattern inQUERY_KEYSand the trailing comma improves consistency.
2684-2697: LGTM on the API migration.The refactor from local filtering to using
api.getRecommendations(limit)simplifies the data flow. The caching of individual accounts for follow mutations is preserved, and the null return for empty results is a reasonable design choice.
2722-2735: Verifyretry: falseis intentional for topics.With
retry: false, if the topics endpoint fails (e.g., temporary network issue), the query won't automatically retry, potentially leaving users with only the "Following" topic until a manual refresh or cache expiration.If topics are critical to the explore experience, consider allowing at least one retry, or ensure the UI gracefully degrades (which it appears to do by showing "Following" only).
There was a problem hiding this comment.
This is the final PR Bugbot will review for you during this billing cycle
Your free Bugbot reviews will reset on January 3
Details
Your team is on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle for each member of your team.
To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
apps/activitypub/src/hooks/use-activity-pub-queries.ts (1)
96-96: Consider following the established pattern for parameterless query keys.The
topicskey is defined as a function() => ['topics'], but other parameterless keys in this object (feed,inbox,discoveryFeed,postsByAccount,postsLikedByAccount) are defined as plain arrays. For consistency, consider changing this to:- topics: () => ['topics'] + topics: ['topics']Then update the usage on line 2512 from
QUERY_KEYS.topics()toQUERY_KEYS.topics.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
apps/activitypub/src/hooks/use-activity-pub-queries.ts(4 hunks)
🧰 Additional context used
🧠 Learnings (6)
📚 Learning: 2025-11-05T16:49:21.124Z
Learnt from: rob-ghost
Repo: TryGhost/Ghost PR: 25356
File: apps/admin/src/hooks/user-preferences.ts:20-20
Timestamp: 2025-11-05T16:49:21.124Z
Learning: In apps/admin/src/hooks/user-preferences.ts, the query key for user preferences intentionally includes `user?.accessibility` (not just `user?.id`) so the cache automatically reacts to changes from any source (useEditUserPreferences, other editUser calls, external updates). Combined with `cacheTime: 0`, this design garbage-collects orphaned entries while keeping the active entry cached. This is a documented, intentional architectural decision.
Applied to files:
apps/activitypub/src/hooks/use-activity-pub-queries.ts
📚 Learning: 2025-03-13T09:02:50.102Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/views/Feed/components/NewPostModal.tsx:29-34
Timestamp: 2025-03-13T09:02:50.102Z
Learning: In the ActivityPub module, error handling for mutations is handled at the hook level (in use-activity-pub-queries.ts) rather than in individual components. This allows for centralized error handling across the application.
Applied to files:
apps/activitypub/src/hooks/use-activity-pub-queries.ts
📚 Learning: 2025-03-04T17:19:50.908Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22330
File: apps/admin-x-activitypub/src/hooks/use-activity-pub-queries.ts:911-912
Timestamp: 2025-03-04T17:19:50.908Z
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.
Applied to files:
apps/activitypub/src/hooks/use-activity-pub-queries.ts
📚 Learning: 2025-03-13T09:02:50.102Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/views/Feed/components/NewPostModal.tsx:29-34
Timestamp: 2025-03-13T09:02:50.102Z
Learning: In the Ghost ActivityPub module, error handling for mutations is handled at the hook level (in use-activity-pub-queries.ts) rather than in individual components. This allows for centralized error handling across the application.
Applied to files:
apps/activitypub/src/hooks/use-activity-pub-queries.ts
📚 Learning: 2025-03-13T09:00:20.205Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/utils/pending-activity.ts:13-71
Timestamp: 2025-03-13T09:00:20.205Z
Learning: The pending activity utilities in Ghost's ActivityPub module are thoroughly tested in `apps/admin-x-activitypub/test/unit/utils/pending-activity.ts`, which covers `generatePendingActivityId`, `isPendingActivity`, and `generatePendingActivity` functions.
Applied to files:
apps/activitypub/src/hooks/use-activity-pub-queries.ts
📚 Learning: 2025-03-13T09:00:20.205Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/utils/pending-activity.ts:13-71
Timestamp: 2025-03-13T09:00:20.205Z
Learning: The pending activity utilities in the Ghost ActivityPub module are covered by tests in the file `apps/admin-x-activitypub/test/unit/utils/pending-activity.ts`.
Applied to files:
apps/activitypub/src/hooks/use-activity-pub-queries.ts
🧬 Code graph analysis (1)
apps/activitypub/src/hooks/use-activity-pub-queries.ts (1)
apps/activitypub/src/api/activitypub.ts (1)
Account(9-31)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Unit tests (Node 22.18.0)
- GitHub Check: ActivityPub tests
- GitHub Check: Lint
- GitHub Check: Cursor Bugbot
- GitHub Check: Build & Push
🔇 Additional comments (3)
apps/activitypub/src/hooks/use-activity-pub-queries.ts (3)
27-27: LGTM: Unused import removed.Good cleanup removing the unused
useMemoimport.
510-523: Correct the line numbers: this review applies to lines 2510-2523, not 510-523.The implementation follows the established patterns with appropriate caching (24-hour stale time for infrequently changing topics) and error handling (
retry: falsefor graceful degradation). ThegetTopicsAPI method is correctly called and the hook properly returns the query object.Likely an incorrect or invalid review comment.
472-489: The review comment is incorrectly applied to the wrong line numbers.Lines 472-489 contain notification post replyCount update logic, not the
useSuggestedProfilesForUserhook. The hook is defined at line 2465. Please verify you've targeted the correct lines for this review.Likely an incorrect or invalid review comment.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
apps/activitypub/src/views/Feed/components/SuggestedProfiles.tsx (2)
19-30: LGTM! Consider wrapping in useCallback for optimization.The scroll button visibility logic is correct and properly guards against null references.
To avoid recreating this function on every render, consider wrapping it in
useCallback:- const updateScrollButtons = () => { + const updateScrollButtons = useCallback(() => { const container = scrollContainerRef.current; if (!container) { return; } const canScrollL = container.scrollLeft > 0; const canScrollR = container.scrollLeft < container.scrollWidth - container.clientWidth; setCanScrollLeft(canScrollL); setCanScrollRight(canScrollR); - }; + }, []);
115-115: Consider debouncing the scroll handler.The scroll handler triggers on every scroll event, which could cause multiple state updates during rapid scrolling. While React batches updates, debouncing would further improve performance.
You can use a debounce utility or implement a simple version:
import {useCallback} from 'react'; // Create a debounced version const debouncedUpdateScrollButtons = useCallback( debounce(updateScrollButtons, 50), [updateScrollButtons] ); // Then use it: onScroll={debouncedUpdateScrollButtons}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
apps/activitypub/src/views/Feed/components/SuggestedProfiles.tsx(3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
apps/activitypub/src/views/Feed/components/SuggestedProfiles.tsx (1)
apps/activitypub/src/api/activitypub.ts (1)
Account(9-31)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Merge React Reports
- GitHub Check: Merge Ember Reports
- GitHub Check: ActivityPub tests
- GitHub Check: Unit tests (Node 22.18.0)
- GitHub Check: Cursor Bugbot
🔇 Additional comments (2)
apps/activitypub/src/views/Feed/components/SuggestedProfiles.tsx (2)
45-55: LGTM! Correctly removes local followerCount mutation.The follow/unfollow handlers now only toggle
followedByMestatus, aligning with the PR objective to stop locally mutating follower counts. The backend will handle the actual follower count updates.
154-154: LGTM! Spacing adjustment for removed follower count.The
mb-6class adds appropriate spacing now that the follower count display has been removed, maintaining the visual consistency of the profile cards.
03c482b to
1e95da5
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
apps/activitypub/package.json(1 hunks)apps/activitypub/src/api/activitypub.ts(2 hunks)apps/activitypub/src/components/TopicFilter.tsx(3 hunks)apps/activitypub/src/components/global/SuggestedProfiles.tsx(2 hunks)apps/activitypub/src/components/layout/Layout.tsx(3 hunks)apps/activitypub/src/components/layout/Onboarding/Step3.tsx(3 hunks)apps/activitypub/src/components/layout/Sidebar/Sidebar.tsx(3 hunks)apps/activitypub/src/components/modals/Search.tsx(3 hunks)apps/activitypub/src/hooks/use-activity-pub-queries.ts(4 hunks)apps/activitypub/src/views/Feed/components/SuggestedProfiles.tsx(4 hunks)apps/activitypub/src/views/Inbox/components/InboxList.tsx(4 hunks)
✅ Files skipped from review due to trivial changes (1)
- apps/activitypub/package.json
🚧 Files skipped from review as they are similar to previous changes (5)
- apps/activitypub/src/views/Inbox/components/InboxList.tsx
- apps/activitypub/src/components/layout/Layout.tsx
- apps/activitypub/src/api/activitypub.ts
- apps/activitypub/src/views/Feed/components/SuggestedProfiles.tsx
- apps/activitypub/src/components/modals/Search.tsx
🧰 Additional context used
🧠 Learnings (11)
📚 Learning: 2025-11-06T05:35:41.162Z
Learnt from: danielraffel
Repo: TryGhost/Ghost PR: 25366
File: apps/admin/src/layout/app-sidebar/NavHeader.tsx:13-23
Timestamp: 2025-11-06T05:35:41.162Z
Learning: In apps/admin/src/layout/app-sidebar/NavHeader.tsx, the React component dispatches a synthetic KeyboardEvent to trigger the Ember keymaster.js search modal shortcut. This approach is known to have cross-browser reliability issues but was deferred for architectural refactoring in a separate PR. The recommended fix is to expose a global function or custom DOM event from the Ember app instead of relying on synthetic keyboard events with keymaster.js.
Applied to files:
apps/activitypub/src/components/layout/Sidebar/Sidebar.tsx
📚 Learning: 2025-08-11T14:18:31.752Z
Learnt from: niranjan-uma-shankar
Repo: TryGhost/Ghost PR: 24626
File: apps/portal/src/components/pages/AccountHomePage/components/AccountFooter.js:15-15
Timestamp: 2025-08-11T14:18:31.752Z
Learning: In Ghost, Portal components render within iframes while admin components (like gh-member-details.hbs, dashboard/charts/recents.hbs, and posts/post-activity-feed.hbs) do not render in iframes. Therefore, iframe-related navigation issues only affect Portal components.
Applied to files:
apps/activitypub/src/components/layout/Sidebar/Sidebar.tsx
📚 Learning: 2025-09-02T07:55:08.601Z
Learnt from: minimaluminium
Repo: TryGhost/Ghost PR: 24798
File: apps/admin-x-activitypub/src/components/feed/layouts/FeedLayout.tsx:133-138
Timestamp: 2025-09-02T07:55:08.601Z
Learning: In apps/admin-x-activitypub FeedLayout component, renderFeedAttachment for Article-type objects intentionally receives onClick (card click handler) instead of onImageClick to ensure clicking article images opens the article rather than triggering the image lightbox.
Applied to files:
apps/activitypub/src/components/layout/Sidebar/Sidebar.tsx
📚 Learning: 2025-10-27T11:59:33.968Z
Learnt from: peterzimon
Repo: TryGhost/Ghost PR: 25261
File: apps/admin/src/layout/app-sidebar/UserMenu.tsx:24-29
Timestamp: 2025-10-27T11:59:33.968Z
Learning: In apps/admin/src/layout/app-sidebar/UserMenu.tsx, the hardcoded placeholder data (avatar URL, initials "US", name "User Name", email "userexample.com") is a known limitation and should not be flagged for replacement with real user data.
Applied to files:
apps/activitypub/src/components/layout/Sidebar/Sidebar.tsxapps/activitypub/src/components/layout/Onboarding/Step3.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/hooks/**/*.{ts,tsx} : Custom React hooks should be placed in `src/hooks/*`
Applied to files:
apps/activitypub/src/components/TopicFilter.tsx
📚 Learning: 2025-11-05T16:49:21.124Z
Learnt from: rob-ghost
Repo: TryGhost/Ghost PR: 25356
File: apps/admin/src/hooks/user-preferences.ts:20-20
Timestamp: 2025-11-05T16:49:21.124Z
Learning: In apps/admin/src/hooks/user-preferences.ts, the query key for user preferences intentionally includes `user?.accessibility` (not just `user?.id`) so the cache automatically reacts to changes from any source (useEditUserPreferences, other editUser calls, external updates). Combined with `cacheTime: 0`, this design garbage-collects orphaned entries while keeping the active entry cached. This is a documented, intentional architectural decision.
Applied to files:
apps/activitypub/src/hooks/use-activity-pub-queries.ts
📚 Learning: 2025-03-04T17:19:50.908Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22330
File: apps/admin-x-activitypub/src/hooks/use-activity-pub-queries.ts:911-912
Timestamp: 2025-03-04T17:19:50.908Z
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.
Applied to files:
apps/activitypub/src/hooks/use-activity-pub-queries.ts
📚 Learning: 2025-03-13T09:02:50.102Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/views/Feed/components/NewPostModal.tsx:29-34
Timestamp: 2025-03-13T09:02:50.102Z
Learning: In the ActivityPub module, error handling for mutations is handled at the hook level (in use-activity-pub-queries.ts) rather than in individual components. This allows for centralized error handling across the application.
Applied to files:
apps/activitypub/src/hooks/use-activity-pub-queries.ts
📚 Learning: 2025-03-13T09:02:50.102Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/views/Feed/components/NewPostModal.tsx:29-34
Timestamp: 2025-03-13T09:02:50.102Z
Learning: In the Ghost ActivityPub module, error handling for mutations is handled at the hook level (in use-activity-pub-queries.ts) rather than in individual components. This allows for centralized error handling across the application.
Applied to files:
apps/activitypub/src/hooks/use-activity-pub-queries.ts
📚 Learning: 2025-03-13T09:00:20.205Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/utils/pending-activity.ts:13-71
Timestamp: 2025-03-13T09:00:20.205Z
Learning: The pending activity utilities in Ghost's ActivityPub module are thoroughly tested in `apps/admin-x-activitypub/test/unit/utils/pending-activity.ts`, which covers `generatePendingActivityId`, `isPendingActivity`, and `generatePendingActivity` functions.
Applied to files:
apps/activitypub/src/hooks/use-activity-pub-queries.ts
📚 Learning: 2025-03-13T09:00:20.205Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/utils/pending-activity.ts:13-71
Timestamp: 2025-03-13T09:00:20.205Z
Learning: The pending activity utilities in the Ghost ActivityPub module are covered by tests in the file `apps/admin-x-activitypub/test/unit/utils/pending-activity.ts`.
Applied to files:
apps/activitypub/src/hooks/use-activity-pub-queries.ts
🧬 Code graph analysis (4)
apps/activitypub/src/components/layout/Sidebar/Sidebar.tsx (1)
apps/activitypub/src/hooks/use-activity-pub-queries.ts (1)
useTopicsForUser(2510-2523)
apps/activitypub/src/components/layout/Onboarding/Step3.tsx (1)
apps/activitypub/src/hooks/use-activity-pub-queries.ts (1)
useTopicsForUser(2510-2523)
apps/activitypub/src/components/TopicFilter.tsx (1)
apps/activitypub/src/hooks/use-activity-pub-queries.ts (1)
useTopicsForUser(2510-2523)
apps/activitypub/src/hooks/use-activity-pub-queries.ts (1)
apps/activitypub/src/api/activitypub.ts (1)
Account(9-31)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Unit tests (Node 22.18.0)
- GitHub Check: ActivityPub tests
- GitHub Check: Lint
- GitHub Check: Cursor Bugbot
- GitHub Check: Build & Push
🔇 Additional comments (11)
apps/activitypub/src/components/global/SuggestedProfiles.tsx (2)
20-30: LGTM - Follower count mutation removed as intended.The removal of optimistic
followerCountupdates aligns with the PR objective to stop locally mutating follower counts. ThefollowedByMetoggle is sufficient for immediate UI feedback.
82-84: LGTM - Clean conditional rendering.The early return properly waits for loading to complete before checking data emptiness, avoiding premature hiding of the component during initial fetch.
apps/activitypub/src/components/layout/Onboarding/Step3.tsx (1)
321-324: Navigation logic is sound.The conditional navigation approach is appropriate for directing users to the most relevant page based on topic availability.
apps/activitypub/src/hooks/use-activity-pub-queries.ts (3)
95-96: LGTM - Query key follows existing patterns.
2465-2508: LGTM - Clean refactor to API-driven recommendations.Good approach caching individual accounts for follow mutation consistency. The null return for empty results enables proper conditional rendering in consumers.
2510-2523: LGTM - Well-designed topics hook.The 24-hour staleTime is appropriate for infrequently-changing topic data, and
retry: falseprevents unnecessary API calls on failure.apps/activitypub/src/components/layout/Sidebar/Sidebar.tsx (2)
29-31: LGTM - Proper loading state handling.Including
!isLoadingin thehasTopicscheck prevents the flicker issue where the Explore link would briefly show as external before topics load.
79-96: LGTM - Well-structured conditional rendering.Good UX decisions: the external link icon clearly indicates off-site navigation, and proper
rel="noopener noreferrer"ensures security for the new tab.apps/activitypub/src/components/TopicFilter.tsx (3)
3-5: LGTM - Type widening for dynamic topics.Changing
Topictostringis necessary to support API-driven topics. The previous union type would require constant updates as topics change.
14-22: LGTM - Robust topic construction.The hardcoded "Following" topic ensures there's always at least one option even if the API returns empty, providing a fallback for edge cases.
49-58: LGTM - Clean topic rendering.Proper separation of concerns:
slugfor identification and event handling,namefor user-facing display.
| const {topicsQuery} = useTopicsForUser(); | ||
| const {data: topicsData} = topicsQuery; | ||
| const hasTopics = topicsData && topicsData.topics.length > 0; |
There was a problem hiding this comment.
Inconsistent loading state handling compared to Sidebar.
The hasTopics computation doesn't check isLoading, unlike Sidebar.tsx (line 31) which uses !isLoading && topicsData && topicsData.topics.length > 0. If the user clicks "Next" before topics finish loading, hasTopics will be false and they'll navigate to / instead of /explore.
const {topicsQuery} = useTopicsForUser();
-const {data: topicsData} = topicsQuery;
-const hasTopics = topicsData && topicsData.topics.length > 0;
+const {data: topicsData, isLoading} = topicsQuery;
+const hasTopics = !isLoading && topicsData && topicsData.topics.length > 0;📝 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 {topicsQuery} = useTopicsForUser(); | |
| const {data: topicsData} = topicsQuery; | |
| const hasTopics = topicsData && topicsData.topics.length > 0; | |
| const {topicsQuery} = useTopicsForUser(); | |
| const {data: topicsData, isLoading} = topicsQuery; | |
| const hasTopics = !isLoading && topicsData && topicsData.topics.length > 0; |
🤖 Prompt for AI Agents
In apps/activitypub/src/components/layout/Onboarding/Step3.tsx around lines 305
to 307, hasTopics is computed without checking the topicsQuery loading state
which can treat pending data as "no topics" and misroute users; update the logic
to destructure isLoading (or the equivalent loading flag) from topicsQuery and
compute hasTopics as !isLoading && topicsData && topicsData.topics.length > 0 so
it matches Sidebar.tsx behavior and prevents navigating to the wrong route if
the user clicks Next before topics finish loading.
There was a problem hiding this comment.
Bug: Recommendations "Find more" link ignores missing topics check
The "Find more" button in Recommendations.tsx and SuggestedProfiles.tsx always navigates to the internal /explore route, but the Sidebar menu correctly checks hasTopics to decide between internal navigation and an external link. When the topics API returns empty but recommendations exist, users clicking "Find more" will land on an /explore page with an empty TopicFilter (since excludeTopics={['following']} filters out the only hardcoded topic). This creates an inconsistent UX where the main Explore menu link behaves differently from these secondary navigation links.
apps/activitypub/src/views/Feed/components/SuggestedProfiles.tsx#L81-L84
| const handleComplete = async () => { | ||
| await setOnboarded(true); | ||
| navigate('/explore'); | ||
| navigate(hasTopics ? '/explore' : '/'); |
There was a problem hiding this comment.
Bug: Race condition in onboarding navigation when topics query is loading
The handleComplete function navigates based on hasTopics, but unlike Sidebar.tsx which checks isLoading state, Step3.tsx derives hasTopics directly from topicsData without considering the query's loading state. When topicsQuery is still loading, topicsData is undefined, causing hasTopics to be false regardless of whether topics actually exist. This means users completing onboarding before the query finishes will be incorrectly routed to / instead of /explore, even when topics are available.
apps/activitypub/package.json
Outdated
| { | ||
| "name": "@tryghost/activitypub", | ||
| "version": "2.0.5", | ||
| "version": "2.0.6", |
There was a problem hiding this comment.
I think we should ship that change as new major, as this relies on new backend API endpoints
React E2E Tests FailedTo view the Playwright test report locally, run: REPORT_DIR=$(mktemp -d) && gh run download 20049874684 -n playwright-report-react -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR" |
ref TryGhost/Ghost#25646 - Activitypub frontend change in TryGhost/Ghost#25646 relies on two new API endpoints (`/topics` and `/recommendations`), and therefore has been shipped with a new major (3.x) - With this change, we're saying: this version of the ActivityPub API is compatible with the frontend version 3.x - More details on versioning in ADR: https://github.com/TryGhost/ActivityPub/blob/main/adr/0002-frontend-backend-versioning.md
ref https://linear.app/ghost/issue/BER-3098/hide-discovery-feeds-tabs-for-self-hosters
ref https://linear.app/ghost/issue/BER-3041/point-activitypub-explore-to-ghost-explore-axis-for-self-hosters
ref https://linear.app/ghost/issue/BER-3025/update-sidebar-in-feed-recommendations-to-use-new-explore-data
/topicsendpoint instead of hardcoded list/recommendationsendpoint with server-side randomizationNote
Use new API endpoints for topics and recommendations, update hooks and UI to load dynamically, and conditionally show/hide Explore/topic UI (with external link fallback).
getTopics()andgetRecommendations(limit?)toActivityPubAPIwith types (TopicData,GetTopicsResponse,GetRecommendationsResponse).QUERY_KEYS.topicsanduseTopicsForUser().useSuggestedProfilesForUser()to fetch fromgetRecommendations()and cache accounts.TopicFilterreads from/topics(typeTopic = string; usesslug/name).https://explore.ghost.org/social-web.@tryghost/activitypubto3.0.0.Written by Cursor Bugbot for commit 086cb83. This will update automatically on new commits. Configure here.