Skip to content

Conversation

@minimaluminium
Copy link
Member

ref https://linear.app/ghost/issue/BER-2980/easier-follow-button-in-article-view
ref https://linear.app/ghost/issue/BER-2981/follow-button-in-notifications

  • Added FollowButton component support for link variant with "Follow back" text
  • Added follow button in Reader header next to author information
  • Added follow buttons to notifications (singular follow, group follow expanded view, replies, and mentions)

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 1, 2025

Walkthrough

This PR bumps apps/activitypub package version 2.0.2 → 2.0.3, adds optional booleans followedByMe and followsMe to Notification.actor in apps/activitypub/src/api/activitypub.ts, extends FollowButton (props: variant?: 'default'|'link', followsYou?: boolean, new link-style rendering and computed button text), and updates Reader and Notifications views to render FollowButton in various header and actor-list locations with layout adjustments.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • apps/activitypub/src/components/global/FollowButton.tsx — verify new variant/followsYou props, link-variant click handling, and computed labels for all edge cases.
  • apps/activitypub/src/views/Notifications/Notifications.tsx — review layout restructuring for multi-actor groups, per-actor FollowButton conditions (group type and followedByMe), and hover/spacing CSS changes.
  • apps/activitypub/src/views/Inbox/components/Reader.tsx — confirm ProfilePreviewHoverCard placement, header layout changes, and FollowButton visibility gating.
  • apps/activitypub/src/api/activitypub.ts — ensure added optional followedByMe/followsMe fields align with usages and typing across the module.
  • apps/activitypub/package.json — trivial version bump (2.0.2 → 2.0.3).

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately reflects the main changes: adding follow buttons to Reader and Notifications components, which is the primary focus of this changeset.
Description check ✅ Passed The PR description is directly related to the changeset, providing specific details about the follow button additions across multiple components and referencing relevant linear issues.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ap-quick-follow-BER-2980

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

Comment @coderabbitai help to get the list of available commands and usage tips.

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 (2)
apps/activitypub/src/components/global/FollowButton.tsx (1)

10-10: Remove unused type prop from interface.

The type prop is defined in the interface but never used in the component. Consider removing it to clean up the API surface.

 interface FollowButtonProps {
     className?: string;
     following: boolean;
     handle: string;
-    type?: 'primary' | 'secondary';
     variant?: 'default' | 'link';
     followsYou?: boolean;
     onFollow?: () => void;
     onUnfollow?: () => void;
     'data-testid'?: string;
 }
apps/activitypub/src/views/Notifications/Notifications.tsx (1)

385-385: Minor: Reorder Tailwind classes for consistency.

The hover modifier group-hover/item:underline comes before base classes. Consider reordering for conventional Tailwind class ordering (base classes first, then modifiers).

-                                                                            <span className='group-hover/item:underline ml-2 line-clamp-1 text-base font-semibold dark:text-white'>{actor.name}</span>
+                                                                            <span className='ml-2 line-clamp-1 text-base font-semibold dark:text-white group-hover/item:underline'>{actor.name}</span>
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dc7b6c7 and fbaf38e.

📒 Files selected for processing (5)
  • apps/activitypub/package.json (1 hunks)
  • apps/activitypub/src/api/activitypub.ts (1 hunks)
  • apps/activitypub/src/components/global/FollowButton.tsx (4 hunks)
  • apps/activitypub/src/views/Inbox/components/Reader.tsx (2 hunks)
  • apps/activitypub/src/views/Notifications/Notifications.tsx (5 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 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} : Use CVA (Class Variance Authority) for component variants when useful instead of heavy prop configuration

Applied to files:

  • apps/activitypub/src/components/global/FollowButton.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 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.json
📚 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.json
📚 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.json
🧬 Code graph analysis (1)
apps/activitypub/src/views/Notifications/Notifications.tsx (2)
apps/admin-x-framework/src/api/activitypub.ts (1)
  • ActorProperties (29-63)
apps/activitypub/src/utils/handle-profile-click.ts (1)
  • handleProfileClick (5-12)
⏰ 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). (3)
  • GitHub Check: Setup
  • GitHub Check: Build & Push
  • GitHub Check: Setup
🔇 Additional comments (7)
apps/activitypub/package.json (1)

3-3: LGTM!

Version bump to 2.0.3 appropriately reflects the new follow button functionality added in this PR.

apps/activitypub/src/api/activitypub.ts (1)

108-109: LGTM!

The new optional followedByMe and followsMe properties on the notification actor align with the existing Account interface pattern (lines 23-24) and properly support the follow button functionality in notifications.

apps/activitypub/src/views/Inbox/components/Reader.tsx (2)

811-834: LGTM!

The header layout refactoring cleanly separates author information from the follow action. The conditional rendering logic correctly hides the follow button for content authored by the current user and during loading states. The nullish coalescing actor.followedByMe ?? false safely handles potentially undefined values.


829-832: This code already has proper type safety.

The actor object comes from mapPostToActivity(post) which returns a properly typed Activity with an actor field of type ActorProperties. The followedByMe property is explicitly mapped from post.author.followedByMe and is safely accessed using the nullish coalescing operator. No refactoring is required.

apps/activitypub/src/components/global/FollowButton.tsx (1)

66-89: LGTM!

Clean implementation of the link variant with proper conditional styling. The buttonText computation handles all three states (Following, Follow back, Follow) correctly. Event handlers appropriately prevent default and stop propagation to avoid triggering parent click handlers.

apps/activitypub/src/views/Notifications/Notifications.tsx (2)

377-395: LGTM on multi-actor follow buttons.

The per-actor follow buttons in the expanded group view are well-implemented. The conditional rendering correctly shows the button only for follow-type notifications where the actor hasn't been followed yet, and passes followsYou for the "Follow back" text.


420-428: LGTM on single-actor follow button.

The selective application of follow buttons to follow, reply, and mention notification types makes sense—these are person-centric interactions where discovering and following the actor is relevant, unlike like and repost which are content-centric.

@github-actions
Copy link
Contributor

github-actions bot commented Dec 1, 2025

E2E Tests Failed

To view the Playwright test report locally, run:

REPORT_DIR=$(mktemp -d) && gh run download 19816059648 -n playwright-report -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR"

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 (2)
apps/activitypub/src/views/Notifications/Notifications.tsx (2)

333-365: Expanded multi-actor follow list: good interaction model, consider stricter follow-state checks

  • The expanded “show all” area correctly:
    • Isolates its own click handling (row + chevron button both stop propagation).
    • Routes profile clicks through handleProfileClick(actor.handle, navigate) without toggling the group.
    • Renders a per-actor FollowButton only for follow-type groups and when !actor.followedByMe, which matches the “quick follow back” intent.

One small robustness tweak you could consider (optional):

{group.type === 'follow' && actor.followedByMe === false && (
    <FollowButton
        following={false}
        followsYou={actor.followsMe === true}
        handle={actor.handle}
        variant="link"
    />
)}

Using explicit === false/true keeps behavior predictable if followedByMe or followsMe are ever undefined / “unknown” rather than strictly boolean.

Also applies to: 368-395


410-428: Single-actor header follow button: condition and layout look sensible

  • The header now uses a two-column flex layout: description + (for single-actor) timestamp on the left, optional FollowButton on the right. This gives a clear, compact affordance without changing navigation behavior.
  • The condition group.actors.length === 1 && (group.type === 'follow' || group.type === 'reply' || group.type === 'mention') && !group.actors[0].followedByMe matches the product goal of enabling “quick follow” from:
    • direct follow notifications,
    • replies, and
    • mentions,
      while avoiding redundant buttons when you already follow the actor.

As with the multi-actor case, you might optionally tighten the predicate to group.actors[0].followedByMe === false and pass followsYou={group.actors[0].followsMe === true} for clearer handling of undefined values, but the current logic is functionally acceptable.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fbaf38e and 67b09ac.

📒 Files selected for processing (2)
  • apps/activitypub/src/components/global/FollowButton.tsx (4 hunks)
  • apps/activitypub/src/views/Notifications/Notifications.tsx (5 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 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} : Use CVA (Class Variance Authority) for component variants when useful instead of heavy prop configuration

Applied to files:

  • apps/activitypub/src/components/global/FollowButton.tsx
🧬 Code graph analysis (1)
apps/activitypub/src/views/Notifications/Notifications.tsx (3)
apps/admin-x-framework/src/api/activitypub.ts (1)
  • ActorProperties (29-63)
apps/activitypub/src/utils/handle-profile-click.ts (1)
  • handleProfileClick (5-12)
apps/activitypub/src/utils/render-timestamp.tsx (1)
  • renderTimestamp (15-40)
⏰ 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). (3)
  • GitHub Check: Unit tests (Node 22.18.0)
  • GitHub Check: ActivityPub tests
  • GitHub Check: Build & Push
🔇 Additional comments (2)
apps/activitypub/src/components/global/FollowButton.tsx (1)

11-12: Follow-back label and link variant wiring look consistent

  • buttonText correctly prioritizes isFollowing over followsYou, so you never see “Follow back” when already following.
  • Both the default and link variants share handleClick, so optimistic local state and mutation behavior stay consistent.
  • Stopping propagation and preventing default in the link variant matches the default button behavior, which avoids accidental navigation when used inside links/lists.

No functional concerns from this change; the new props and variant behavior look coherent with the existing state model.

Also applies to: 24-26, 66-89, 106-107

apps/activitypub/src/views/Notifications/Notifications.tsx (1)

8-8: Profile preview alignment and FollowButton import are straightforward

Adding the FollowButton import and passing align="center" into ProfilePreviewHoverCard are minimal, self-contained changes; they don’t alter notification logic and keep the hover-card usage consistent.

Also applies to: 122-135

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