Skip to content

Updated activitypub Bluesky sharing enablement flow#21

Open
tomerqodo wants to merge 6 commits intocoderabbit_combined_20260121_qodo_grep_cursor_copilot_base_updated_activitypub_bluesky_sharing_enablement_flow_pr244from
coderabbit_combined_20260121_qodo_grep_cursor_copilot_head_updated_activitypub_bluesky_sharing_enablement_flow_pr244
Open

Updated activitypub Bluesky sharing enablement flow#21
tomerqodo wants to merge 6 commits intocoderabbit_combined_20260121_qodo_grep_cursor_copilot_base_updated_activitypub_bluesky_sharing_enablement_flow_pr244from
coderabbit_combined_20260121_qodo_grep_cursor_copilot_head_updated_activitypub_bluesky_sharing_enablement_flow_pr244

Conversation

@tomerqodo
Copy link

@tomerqodo tomerqodo commented Jan 21, 2026

Benchmark PR from qodo-benchmark#244

Summary by CodeRabbit

  • New Features

    • Added a new Bluesky handle confirmation workflow with polling-based verification.
    • Introduced improved confirmation UI with detailed loading states and status messaging.
    • Added ability to view and open Bluesky profiles directly from the app.
  • Bug Fixes

    • Updated Bluesky API endpoints to latest version.
  • Chores

    • Bumped version to 2.0.0.

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

mike182uk and others added 6 commits January 21, 2026 15:51
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 21, 2026

Walkthrough

This pull request introduces a new Bluesky handle confirmation workflow by upgrading API endpoints from v1 to v2, refactoring the Account type to include confirmation state, separating handle confirmation into a dedicated method, and enhancing the UI component with polling-based confirmation logic. The package version is bumped to 2.0.0 and TypeScript strict mode is disabled.

Changes

Cohort / File(s) Summary
API Updates
apps/activitypub/src/api/activitypub.ts, apps/activitypub/src/api/activitypub.test.ts
Bluesky API endpoints upgraded from v1 to v2. enableBluesky() and disableBluesky() now return Promise<void> instead of string handles. New confirmBlueskyHandle() method added to explicitly retrieve and confirm handles. Account interface updated to include blueskyHandleConfirmed?: boolean and blueskyHandle?: string | null.
Hook Refactoring
apps/activitypub/src/hooks/use-activity-pub-queries.ts
Introduced BlueskyDetails type to encapsulate Bluesky state (enabled, confirmed, handle). updateAccountBlueskyCache() signature changed to accept BlueskyDetails object instead of individual parameters. useEnableBlueskyMutationForUser and useDisableBlueskyMutationForUser onSuccess callbacks now receive no arguments and manage state via the new pattern. New useConfirmBlueskyHandleMutationForUser() hook added for handle confirmation with cache updates.
UI Component Enhancement
apps/activitypub/src/views/Preferences/components/BlueskySharing.tsx
Added polling-based handle confirmation workflow with handleConfirmed state and configurable retry/interval constants. Integrated useConfirmBlueskyHandleMutationForUser mutation. Refactored loading logic to derive from account state flags. Enhanced UI to show distinct flows for setup, enabling, confirming, and confirmed states with appropriate messaging and actions.
Configuration & Version
apps/activitypub/package.json, apps/activitypub/tsconfig.json
Version bumped from 1.1.0 to 2.0.0. TypeScript strict mode changed from true to false.

Sequence Diagram

sequenceDiagram
    participant User as User (UI)
    participant Component as BlueskySharing Component
    participant API as ActivityPubAPI
    participant QueryClient as Query Client
    participant Endpoint as v2 Endpoint

    User->>Component: Enable Bluesky
    Component->>API: enableBluesky()
    API->>Endpoint: POST /v2/actions/bluesky/enable
    Endpoint-->>API: 200 OK
    API-->>Component: void
    Component->>QueryClient: updateAccountBlueskyCache({enabled: true, confirmed: false})
    Component->>Component: Start polling effect

    loop Polling (max retries)
        Component->>API: confirmBlueskyHandle(handle)
        API->>Endpoint: GET /v2/actions/bluesky/confirm-handle
        Endpoint-->>API: {handle: "user.bsky.social"}
        API-->>Component: "user.bsky.social"
        Component->>QueryClient: updateAccountBlueskyCache({enabled: true, confirmed: true, handle: "user.bsky.social"})
        Component->>Component: Stop polling, show success toast
        Component->>User: Display confirmed Bluesky profile
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Poem

🐰 Hops of joy for Bluesky's v2 way,
Confirms with polls throughout the day,
No more strings from enable's call,
New state shapes embrace it all!
Strict mode loosens, workflows flow,
Watch our Bluesky platform grow! 🚀

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: updates to the Bluesky sharing enablement flow, including backend confirmation steps and new confirmation polling workflow.

✏️ 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: 1

🤖 Fix all issues with AI agents
In `@apps/activitypub/src/views/Preferences/components/BlueskySharing.tsx`:
- Around line 103-117: The interval callback clears the interval and calls await
disableBlueskyMutation.mutateAsync() then setLoading(false), which can race with
unmounts or interval clears causing state updates on an unmounted component;
update the logic in the confirmHandleInterval handler (referencing
confirmHandleInterval, retryCountRef, MAX_CONFIRMATION_RETRIES,
CONFIRMATION_INTERVAL, confirmHandle) to either (a) use the mutation's
onSettled/onSuccess/onError callbacks to call setLoading(false) (and clear the
interval there), or (b) track component mounted state with a ref (e.g.,
isMountedRef) and check it before calling setLoading(false) after awaiting
disableBlueskyMutation.mutateAsync(), ensuring the mutation is awaited and you
only call setLoading when mounted and after the mutation settles.
🧹 Nitpick comments (5)
apps/activitypub/tsconfig.json (1)

18-18: Re-enable strict to avoid masking type regressions.

Turning off strict mode broadly weakens type guarantees and can hide issues introduced by the new ActivityPub API changes. Prefer restoring "strict": true and fixing or narrowly suppressing the specific type errors (e.g., per-file // @ts-expect-error`` or selective flags) rather than disabling strict globally.

♻️ Suggested change
-    "strict": false,
+    "strict": true,

[recommendation] Please confirm the concrete plan to restore strict mode (and/or which specific strict flags need relaxation) so this doesn’t become a permanent downgrade.

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

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

On line 732, the String(json.handle) call is redundant since the type guard on line 728 already confirms json.handle is a string.

Suggested simplification
-        return String(json.handle);
+        return json.handle;
apps/activitypub/src/hooks/use-activity-pub-queries.ts (1)

2810-2841: Address the TODO comment or remove it.

Line 2833 contains a comment noting missing invalidation of accountFollows query. The useEnableBlueskyMutationForUser and useDisableBlueskyMutationForUser hooks both invalidate this query, so it seems intentional to include it here as well for consistency when the handle is confirmed.

Would you like me to add the missing invalidation, or should this comment be removed if the invalidation is intentionally omitted?

Suggested fix if invalidation is needed
             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')
+            });
         },
apps/activitypub/src/views/Preferences/components/BlueskySharing.tsx (2)

68-75: Consider removing the eslint-disable or refactoring.

The confirmHandle callback has an empty dependency array with an eslint-disable comment. While React Query mutations are stable in practice, this pattern can be fragile. Consider using useCallback without disabling the lint rule:

Alternative approach
-    const confirmHandle = useCallback(() => {
-        confirmBlueskyHandleMutation.mutateAsync().then((handle) => {
-            if (handle) {
-                setHandleConfirmed(true);
-            }
-        });
-        // eslint-disable-next-line react-hooks/exhaustive-deps
-    }, []); // Empty deps - mutations are stable in practice
+    const confirmHandle = useCallback(() => {
+        confirmBlueskyHandleMutation.mutateAsync().then((handle) => {
+            if (handle) {
+                setHandleConfirmed(true);
+            }
+        });
+    }, [confirmBlueskyHandleMutation]);

Since confirmBlueskyHandleMutation is indeed stable (returned from useMutation), including it won't cause extra re-renders but will satisfy the linter.


77-121: Verify: Initial polling call timing.

The polling effect starts with retryCountRef.current = 0 on line 101, then the first confirmHandle() call happens after 5 seconds (CONFIRMATION_INTERVAL). This means users wait 5 seconds before the first confirmation attempt.

Consider calling confirmHandle() immediately when Bluesky is enabled but not yet confirmed, then starting the interval for subsequent retries.

Suggested improvement for faster initial confirmation
         setHandleConfirmed(false);
         setLoading(true);
         retryCountRef.current = 0;

+        // Attempt confirmation immediately
+        confirmHandle();
+
         const confirmHandleInterval = setInterval(async () => {
             if (retryCountRef.current >= MAX_CONFIRMATION_RETRIES) {
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f33fdc6 and ef8b79a.

📒 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 (1)
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)
🔇 Additional comments (11)
apps/activitypub/package.json (1)

3-3: LGTM!

The major version bump to 2.0.0 is appropriate given the breaking API changes (v1→v2 endpoints, modified Account type with nullable blueskyHandle, and the new confirmation flow).

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

29-30: LGTM!

The Account interface updates properly support the new Bluesky confirmation flow with blueskyHandleConfirmed flag and nullable blueskyHandle.


711-721: LGTM!

The enableBluesky and disableBluesky methods are correctly updated to use v2 endpoints. The return type change from Promise<string> to Promise<void> aligns with the new flow where the handle is obtained separately via confirmBlueskyHandle.

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

1600-1627: LGTM!

Test correctly verifies the enableBluesky method calls the v2 endpoint with POST and no longer expects a return value.


1629-1656: LGTM!

Test correctly verifies the new disableBluesky method calls the v2 endpoint with POST.


1658-1767: LGTM!

Comprehensive test coverage for confirmBlueskyHandle including:

  • Successful handle confirmation
  • Null response handling
  • Missing handle property
  • Invalid handle type (array instead of string)
apps/activitypub/src/hooks/use-activity-pub-queries.ts (2)

2725-2744: LGTM!

The BlueskyDetails type and refactored updateAccountBlueskyCache function cleanly encapsulate the Bluesky state fields and provide a more maintainable cache update pattern.


2746-2776: LGTM!

The useEnableBlueskyMutationForUser correctly sets blueskyHandleConfirmed: false on success, properly reflecting the intermediate state before handle confirmation completes.

apps/activitypub/src/views/Preferences/components/BlueskySharing.tsx (3)

27-28: LGTM!

The polling constants (5s interval, 12 retries = 60s max) provide a reasonable timeout for handle confirmation while keeping the interval balanced between responsiveness and server load.


138-149: LGTM!

The showAsEnabled computed flag correctly gates the UI based on both blueskyEnabled AND blueskyHandleConfirmed, ensuring a consistent display state.


190-228: LGTM!

The confirmed-state UI section properly displays the Bluesky profile card with avatar, handle, copy functionality, and external profile link. The conditional rendering on handleConfirmed ensures this only shows after successful confirmation.

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

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

Potential race condition: mutation not awaited before state update.

On line 109, disableBlueskyMutation.mutateAsync() is awaited, but if the interval is cleared (e.g., by component unmount or state change) while the mutation is in-flight, the subsequent setLoading(false) on line 110 may not execute as expected, or may execute on an unmounted component.

Consider adding a mounted ref check or using the mutation's onSettled callback instead.

Suggested improvement
+    const mountedRef = useRef(true);
+
+    useEffect(() => {
+        return () => {
+            mountedRef.current = false;
+        };
+    }, []);
+
     // In the interval callback:
             if (retryCountRef.current >= MAX_CONFIRMATION_RETRIES) {
                 clearInterval(confirmHandleInterval);

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

-                await disableBlueskyMutation.mutateAsync();
-                setLoading(false);
+                disableBlueskyMutation.mutateAsync().finally(() => {
+                    if (mountedRef.current) {
+                        setLoading(false);
+                    }
+                });

                 return;
             }
🤖 Prompt for AI Agents
In `@apps/activitypub/src/views/Preferences/components/BlueskySharing.tsx` around
lines 103 - 117, The interval callback clears the interval and calls await
disableBlueskyMutation.mutateAsync() then setLoading(false), which can race with
unmounts or interval clears causing state updates on an unmounted component;
update the logic in the confirmHandleInterval handler (referencing
confirmHandleInterval, retryCountRef, MAX_CONFIRMATION_RETRIES,
CONFIRMATION_INTERVAL, confirmHandle) to either (a) use the mutation's
onSettled/onSuccess/onError callbacks to call setLoading(false) (and clear the
interval there), or (b) track component mounted state with a ref (e.g.,
isMountedRef) and check it before calling setLoading(false) after awaiting
disableBlueskyMutation.mutateAsync(), ensuring the mutation is awaited and you
only call setLoading when mounted and after the mutation settles.

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