Skip to content

Comments

Updated activitypub Bluesky sharing enablement flow#48

Open
tomerqodo wants to merge 6 commits intocoderabbit_full_base_updated_activitypub_bluesky_sharing_enablement_flow_pr10from
coderabbit_full_head_updated_activitypub_bluesky_sharing_enablement_flow_pr10
Open

Updated activitypub Bluesky sharing enablement flow#48
tomerqodo wants to merge 6 commits intocoderabbit_full_base_updated_activitypub_bluesky_sharing_enablement_flow_pr10from
coderabbit_full_head_updated_activitypub_bluesky_sharing_enablement_flow_pr10

Conversation

@tomerqodo
Copy link

@tomerqodo tomerqodo commented Jan 28, 2026

Benchmark PR from agentic-review-benchmarks#10

Summary by CodeRabbit

  • New Features

    • Added Bluesky handle confirmation workflow with real-time polling and automatic status updates.
    • Enhanced user interface with confirmation badges when Bluesky is successfully connected.
  • Improvements

    • Refined Bluesky setup process with improved loading indicators and user feedback.
    • Better connection status visibility and error handling throughout the integration.

✏️ Tip: You can customize this high-level summary in your review settings.

mike182uk and others added 6 commits January 25, 2026 11:57
ref TryGhost/ActivityPub#1377

We updated the backend implementation of the Bluesky sharing enablement process
to include a confirmation step in which the client would prompt the backend
to check what handle has been assigned to the Bluesky account generated by
Bridgy Fed - This circumvents the need to for us to compute the handle manually
and potentially getting it wrong if we compute it in a different way to Bridgy
Fed
@coderabbitai
Copy link

coderabbitai bot commented Jan 28, 2026

Caution

Review failed

Failed to post review comments

Walkthrough

The pull request refactors the Bluesky integration from a single-step enable process to a two-step flow: enable followed by handle confirmation. API method signatures for enableBluesky() and disableBluesky() now return Promise<void> instead of handle strings. A new confirmBlueskyHandle() method retrieves the confirmed handle. UI component introduces confirmation polling with retry logic. Version bumped to 2.0.0, TypeScript strict mode disabled.

Changes

Cohort / File(s) Summary
Version & Configuration
apps/activitypub/package.json, apps/activitypub/tsconfig.json
Version bumped from 1.1.0 to 2.0.0; TypeScript strict mode disabled (set to false).
Core API Layer
apps/activitypub/src/api/activitypub.ts, apps/activitypub/src/api/activitypub.test.ts
enableBluesky() and disableBluesky() now return Promise<void> instead of handle strings; new confirmBlueskyHandle(): Promise<string> method added; Account interface extended with blueskyHandle?: string | null and blueskyHandleConfirmed?: boolean; endpoints migrated from v1 to v2; test mocks and assertions updated accordingly.
State Management & Hooks
apps/activitypub/src/hooks/use-activity-pub-queries.ts
New BlueskyDetails type introduced to group Bluesky state; updateAccountBlueskyCache() refactored to accept BlueskyDetails object; useEnableBlueskyMutationForUser() and useDisableBlueskyMutationForUser() updated with no-arg onSuccess callbacks; new useConfirmBlueskyHandleMutationForUser() hook added to handle confirmation polling.
UI Component Enhancement
apps/activitypub/src/views/Preferences/components/BlueskySharing.tsx
Introduced confirmation polling with configurable interval and retry limits; added loading state management tied to confirmation progress; showAsEnabled derived state replaces simple enabled check; new conditional rendering for confirmed state with avatar badge and profile link; polling effect syncs with account status and triggers disable on retry exhaustion.

Sequence Diagram

sequenceDiagram
    actor User
    participant BlueskySharing as BlueskySharing Component
    participant useEnableMutation as useEnableBlueskyMutation
    participant useConfirmMutation as useConfirmBlueskyHandleMutation
    participant API as ActivityPubAPI
    participant Backend as Backend (v2 endpoints)

    User->>BlueskySharing: Click Enable Bluesky
    BlueskySharing->>useEnableMutation: trigger enableBluesky()
    useEnableMutation->>API: enableBluesky()
    API->>Backend: POST /v2/enable
    Backend-->>API: 200 OK
    API-->>useEnableMutation: onSuccess (void)
    useEnableMutation-->>BlueskySharing: update cache (enabled: true, confirmed: false)
    BlueskySharing->>BlueskySharing: start polling effect
    
    loop Polling (interval, max retries)
        BlueskySharing->>useConfirmMutation: trigger confirmBlueskyHandle()
        useConfirmMutation->>API: confirmBlueskyHandle()
        API->>Backend: POST /v2/confirm-handle
        Backend-->>API: return handle string
        API-->>useConfirmMutation: onSuccess with handle
        useConfirmMutation-->>BlueskySharing: update cache (confirmed: true, handle: string)
        BlueskySharing->>BlueskySharing: stop polling, show badge + profile link
    end

    User->>BlueskySharing: Click Disable Bluesky
    BlueskySharing->>useEnableMutation: trigger disableBluesky()
    useEnableMutation->>API: disableBluesky()
    API->>Backend: POST /v2/disable
    Backend-->>API: 200 OK
    API-->>useEnableMutation: onSuccess (void)
    useEnableMutation-->>BlueskySharing: update cache (enabled: false, confirmed: false)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~35 minutes

Poem

🐰 Hops with glee through confirmation flows,
Two-step dances where the Bluesky handle grows,
Polling away, with badges so bright,
From enable to confirm, a seamless flight!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main change: updating the Bluesky sharing enablement flow with a new two-step confirmation process and API version migration.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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

Copy link

@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: 4

🤖 Fix all issues with AI agents
In `@apps/activitypub/src/hooks/use-activity-pub-queries.ts`:
- Around line 2731-2743: The cached profile update in updateAccountBlueskyCache
incorrectly types the query callback param as BlueskyDetails; change the
callback parameter type to the full Account | undefined (or a union that matches
what's stored in the cache) so currentProfile reflects the actual cached shape,
and update the function signature/any related local types to use Account where
appropriate (keep using QUERY_KEYS.account('index') and the same
QueryClient.setQueryData call). Ensure the runtime guard (if (!currentProfile)
return currentProfile) remains valid for undefined cache entries.
- Around line 2827-2834: The updateAccountBlueskyCache call currently updates
the Bluesky flags but skips invalidating the accountFollows cache; add the same
invalidation used in useEnableBlueskyMutationForUser and
useDisableBlueskyMutationForUser so the UI refreshes—after calling
updateAccountBlueskyCache in the success handler, call the
queryClient.invalidateQueries for the accountFollows query key (the same
key/name those two hooks use) to force refetch/update of follow state.

In `@apps/activitypub/src/views/Preferences/components/BlueskySharing.tsx`:
- Around line 103-117: The polling interval clears when retries exceed
MAX_CONFIRMATION_RETRIES but the async call disableBlueskyMutation.mutateAsync()
is not inside a try/catch, so failures are unhandled; update the
confirmHandleInterval callback (where confirmHandle() is invoked and
retryCountRef is incremented) to await disableBlueskyMutation.mutateAsync()
inside a try/catch, handle errors by logging and/or showing toast.error, and
ensure setLoading(false) runs in a finally block (or after the catch) so loading
state is always cleared.

In `@apps/activitypub/tsconfig.json`:
- Line 18: The tsconfig currently turns off all strict type checking by setting
"strict": false; change this to enable stricter checks—either set "strict": true
in the tsconfig or selectively enable sub-options (e.g., "strictNullChecks",
"strictFunctionTypes", "strictPropertyInitialization") instead of disabling
everything; update the tsconfig entry that currently reads "strict": false and
then fix resulting type errors in code (use type guards, explicit types, or
non-null assertions) in functions/classes that fail under the stricter settings
such as any files flagged by the compiler until the project compiles cleanly.
🧹 Nitpick comments (3)
apps/activitypub/src/api/activitypub.ts (1)

723-733: Minor: redundant String() conversion.

Line 728 already confirms typeof json.handle === 'string', so the String() wrapper on line 732 is unnecessary.

♻️ Suggested simplification
-        return String(json.handle);
+        return json.handle;
apps/activitypub/src/views/Preferences/components/BlueskySharing.tsx (2)

32-32: Initial state may become stale if account data changes.

The state initializer reads from account during the first render, but if account changes (e.g., from undefined to loaded data), the initial state won't update. This could lead to incorrect initial loading state.

♻️ Suggested fix
-    const [loading, setLoading] = useState(() => account?.blueskyEnabled && !account?.blueskyHandleConfirmed);
+    const [loading, setLoading] = useState(false);

The useEffect on lines 77-121 already handles synchronizing the loading state based on account changes, so initializing to false is sufficient.


68-75: Consider adding error handling to confirmHandle.

The confirmBlueskyHandleMutation.mutateAsync() call uses .then() without a .catch(). While the mutation hook handles errors internally (showing rate limit toasts), network errors or unexpected failures could go unhandled.

♻️ Suggested improvement
     const confirmHandle = useCallback(() => {
         confirmBlueskyHandleMutation.mutateAsync().then((handle) => {
             if (handle) {
                 setHandleConfirmed(true);
             }
-        });
+        }).catch(() => {
+            // Errors are handled by the mutation hook
+        });
         // eslint-disable-next-line react-hooks/exhaustive-deps
     }, []); // Empty deps - mutations are stable in practice
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9b6ba0f and 92f3514.

📒 Files selected for processing (6)
  • apps/activitypub/package.json
  • apps/activitypub/src/api/activitypub.test.ts
  • apps/activitypub/src/api/activitypub.ts
  • apps/activitypub/src/hooks/use-activity-pub-queries.ts
  • apps/activitypub/src/views/Preferences/components/BlueskySharing.tsx
  • apps/activitypub/tsconfig.json
🧰 Additional context used
🧬 Code graph analysis (2)
apps/activitypub/src/views/Preferences/components/BlueskySharing.tsx (1)
apps/activitypub/src/hooks/use-activity-pub-queries.ts (4)
  • useAccountForUser (1616-1628)
  • useEnableBlueskyMutationForUser (2746-2776)
  • useDisableBlueskyMutationForUser (2778-2808)
  • useConfirmBlueskyHandleMutationForUser (2810-2841)
apps/activitypub/src/hooks/use-activity-pub-queries.ts (1)
apps/admin-x-framework/src/index.ts (1)
  • useQueryClient (44-44)
🔇 Additional comments (9)
apps/activitypub/package.json (1)

3-3: LGTM!

The major version bump to 2.0.0 is appropriate given the breaking API changes where enableBluesky() no longer returns a handle and the new two-step enable/confirm flow is introduced.

apps/activitypub/src/hooks/use-activity-pub-queries.ts (1)

2725-2729: LGTM!

The BlueskyDetails type cleanly encapsulates the Bluesky state properties needed for cache updates.

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

29-30: LGTM!

The Account interface updates properly reflect the new Bluesky state model with blueskyHandleConfirmed as an optional boolean and blueskyHandle accepting string | null | undefined.


711-721: LGTM!

The enableBluesky and disableBluesky methods are cleanly implemented with the v2 endpoints. The void return type correctly reflects that enable no longer returns a handle.

apps/activitypub/src/api/activitypub.test.ts (3)

1600-1626: LGTM!

The test correctly verifies that enableBluesky sends a POST request to the v2 endpoint and no longer expects a return value.


1629-1656: LGTM!

The disableBluesky test correctly verifies the POST method and v2 endpoint.


1658-1767: LGTM!

Excellent test coverage for confirmBlueskyHandle covering:

  • Successful handle confirmation
  • Null response handling
  • Missing handle property
  • Invalid handle type (array instead of string)
apps/activitypub/src/views/Preferences/components/BlueskySharing.tsx (2)

27-28: LGTM!

The polling constants are reasonable: 5 second intervals with 12 retries gives approximately 60 seconds for handle confirmation, which should accommodate network delays.


190-228: LGTM!

The confirmed state UI is well-structured with the avatar, Bluesky badge overlay, handle display with copy functionality, and the "Open profile" link. The profile URL construction correctly strips the leading @ from the handle.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +2731 to 2743
function updateAccountBlueskyCache(queryClient: QueryClient, blueskyDetails: BlueskyDetails) {
const profileQueryKey = QUERY_KEYS.account('index');

queryClient.setQueryData(profileQueryKey, (currentProfile?: {blueskyEnabled: boolean, blueskyHandle: string | null}) => {
queryClient.setQueryData(profileQueryKey, (currentProfile?: BlueskyDetails) => {
if (!currentProfile) {
return currentProfile;
}

return {
...currentProfile,
blueskyEnabled: blueskyHandle !== null,
blueskyHandle
...blueskyDetails
};
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Type annotation may be too narrow.

The currentProfile parameter is typed as BlueskyDetails but the cached data is a full Account object. This could cause type confusion or silent failures if the cache is empty/undefined.

♻️ Suggested fix
-    queryClient.setQueryData(profileQueryKey, (currentProfile?: BlueskyDetails) => {
+    queryClient.setQueryData(profileQueryKey, (currentProfile?: Account) => {
🤖 Prompt for AI Agents
In `@apps/activitypub/src/hooks/use-activity-pub-queries.ts` around lines 2731 -
2743, The cached profile update in updateAccountBlueskyCache incorrectly types
the query callback param as BlueskyDetails; change the callback parameter type
to the full Account | undefined (or a union that matches what's stored in the
cache) so currentProfile reflects the actual cached shape, and update the
function signature/any related local types to use Account where appropriate
(keep using QUERY_KEYS.account('index') and the same QueryClient.setQueryData
call). Ensure the runtime guard (if (!currentProfile) return currentProfile)
remains valid for undefined cache entries.

Comment on lines +2827 to +2834
updateAccountBlueskyCache(queryClient, {
blueskyEnabled: true,
blueskyHandleConfirmed: true,
blueskyHandle: blueskyHandle
});

// Note: Missing invalidation of accountFollows query that should happen here
},
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Address the TODO: missing cache invalidation.

The comment on line 2833 notes that accountFollows invalidation is missing. Both useEnableBlueskyMutationForUser and useDisableBlueskyMutationForUser invalidate this query on success (lines 2766-2768, 2797-2799). For consistency and to ensure the UI reflects the latest state, this invalidation should be added here.

♻️ Suggested fix
             updateAccountBlueskyCache(queryClient, {
                 blueskyEnabled: true,
                 blueskyHandleConfirmed: true,
                 blueskyHandle: blueskyHandle
             });

-            // Note: Missing invalidation of accountFollows query that should happen here
+            queryClient.invalidateQueries({
+                queryKey: QUERY_KEYS.accountFollows('index', 'following')
+            });
         },

Do you want me to open an issue to track this task?

📝 Committable suggestion

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

Suggested change
updateAccountBlueskyCache(queryClient, {
blueskyEnabled: true,
blueskyHandleConfirmed: true,
blueskyHandle: blueskyHandle
});
// Note: Missing invalidation of accountFollows query that should happen here
},
updateAccountBlueskyCache(queryClient, {
blueskyEnabled: true,
blueskyHandleConfirmed: true,
blueskyHandle: blueskyHandle
});
queryClient.invalidateQueries({
queryKey: QUERY_KEYS.accountFollows('index', 'following')
});
},
🤖 Prompt for AI Agents
In `@apps/activitypub/src/hooks/use-activity-pub-queries.ts` around lines 2827 -
2834, The updateAccountBlueskyCache call currently updates the Bluesky flags but
skips invalidating the accountFollows cache; add the same invalidation used in
useEnableBlueskyMutationForUser and useDisableBlueskyMutationForUser so the UI
refreshes—after calling updateAccountBlueskyCache in the success handler, call
the queryClient.invalidateQueries for the accountFollows query key (the same
key/name those two hooks use) to force refetch/update of follow state.

Comment on lines +103 to +117
const confirmHandleInterval = setInterval(async () => {
if (retryCountRef.current >= MAX_CONFIRMATION_RETRIES) {
clearInterval(confirmHandleInterval);

toast.error('Something went wrong, please try again.');

await disableBlueskyMutation.mutateAsync();
setLoading(false);

return;
}

retryCountRef.current += 1;
confirmHandle();
}, CONFIRMATION_INTERVAL);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Missing error handling for async operations in polling.

The disableBlueskyMutation.mutateAsync() call on line 109 is not wrapped in try/catch. If this mutation fails, the interval would already be cleared but the error would be unhandled, potentially causing silent failures.

♻️ Suggested fix
         const confirmHandleInterval = setInterval(async () => {
             if (retryCountRef.current >= MAX_CONFIRMATION_RETRIES) {
                 clearInterval(confirmHandleInterval);

                 toast.error('Something went wrong, please try again.');

-                await disableBlueskyMutation.mutateAsync();
-                setLoading(false);
+                try {
+                    await disableBlueskyMutation.mutateAsync();
+                } catch {
+                    // Error toast already shown by mutation
+                } finally {
+                    setLoading(false);
+                }

                 return;
             }

             retryCountRef.current += 1;
             confirmHandle();
         }, CONFIRMATION_INTERVAL);
📝 Committable suggestion

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

Suggested change
const confirmHandleInterval = setInterval(async () => {
if (retryCountRef.current >= MAX_CONFIRMATION_RETRIES) {
clearInterval(confirmHandleInterval);
toast.error('Something went wrong, please try again.');
await disableBlueskyMutation.mutateAsync();
setLoading(false);
return;
}
retryCountRef.current += 1;
confirmHandle();
}, CONFIRMATION_INTERVAL);
const confirmHandleInterval = setInterval(async () => {
if (retryCountRef.current >= MAX_CONFIRMATION_RETRIES) {
clearInterval(confirmHandleInterval);
toast.error('Something went wrong, please try again.');
try {
await disableBlueskyMutation.mutateAsync();
} catch {
// Error toast already shown by mutation
} finally {
setLoading(false);
}
return;
}
retryCountRef.current += 1;
confirmHandle();
}, CONFIRMATION_INTERVAL);
🤖 Prompt for AI Agents
In `@apps/activitypub/src/views/Preferences/components/BlueskySharing.tsx` around
lines 103 - 117, The polling interval clears when retries exceed
MAX_CONFIRMATION_RETRIES but the async call disableBlueskyMutation.mutateAsync()
is not inside a try/catch, so failures are unhandled; update the
confirmHandleInterval callback (where confirmHandle() is invoked and
retryCountRef is incremented) to await disableBlueskyMutation.mutateAsync()
inside a try/catch, handle errors by logging and/or showing toast.error, and
ensure setLoading(false) runs in a finally block (or after the catch) so loading
state is always cleared.


/* Linting */
"strict": true,
"strict": false,
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Disabling strict mode weakens type safety for the entire app.

Setting "strict": false disables important type checks including strictNullChecks, strictFunctionTypes, and strictPropertyInitialization. This is a broad change that could mask type errors throughout the codebase.

Consider enabling specific strict sub-options while disabling only the ones causing issues:

♻️ Suggested approach
-    "strict": false,
+    "strict": true,
+    "strictNullChecks": false,

Or better yet, keep strict mode and fix the type issues with proper type guards/assertions where needed.

📝 Committable suggestion

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

Suggested change
"strict": false,
"strict": true,
"strictNullChecks": false,
🤖 Prompt for AI Agents
In `@apps/activitypub/tsconfig.json` at line 18, The tsconfig currently turns off
all strict type checking by setting "strict": false; change this to enable
stricter checks—either set "strict": true in the tsconfig or selectively enable
sub-options (e.g., "strictNullChecks", "strictFunctionTypes",
"strictPropertyInitialization") instead of disabling everything; update the
tsconfig entry that currently reads "strict": false and then fix resulting type
errors in code (use type guards, explicit types, or non-null assertions) in
functions/classes that fail under the stricter settings such as any files
flagged by the compiler until the project compiles cleanly.

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.

3 participants