Skip to content

Updated activitypub Bluesky sharing enablement flow#23

Open
tomerqodo wants to merge 6 commits intoqodo_combined_20260121_qodo_grep_cursor_copilot_1_base_updated_activitypub_bluesky_sharing_enablement_flow_pr244from
qodo_combined_20260121_qodo_grep_cursor_copilot_1_head_updated_activitypub_bluesky_sharing_enablement_flow_pr244
Open

Updated activitypub Bluesky sharing enablement flow#23
tomerqodo wants to merge 6 commits intoqodo_combined_20260121_qodo_grep_cursor_copilot_1_base_updated_activitypub_bluesky_sharing_enablement_flow_pr244from
qodo_combined_20260121_qodo_grep_cursor_copilot_1_head_updated_activitypub_bluesky_sharing_enablement_flow_pr244

Conversation

@tomerqodo
Copy link
Copy Markdown

Benchmark PR from qodo-benchmark#244

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
@qodo-code-review
Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (3) 📎 Requirement gaps (0)

Grey Divider


Action required

1. tsconfig disables strict 📘 Rule violation ✓ Correctness
Description
• The TypeScript config sets compilerOptions.strict to false, weakening compile-time guarantees
  and allowing unsafe typing patterns.
• This violates the requirement to enable strict typing (and, by extension, the associated
  noImplicitAny protections) for TypeScript configurations.
Code

apps/activitypub/tsconfig.json[18]

+    "strict": false,
Evidence
Compliance rule 21 requires TypeScript configs to enable strict and noImplicitAny. The updated
apps/activitypub/tsconfig.json explicitly sets strict to false.

AGENTS.md
apps/activitypub/tsconfig.json[17-21]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`apps/activitypub/tsconfig.json` disables TypeScript strict mode, violating the requirement to keep `strict` (and `noImplicitAny`) enabled.

## Issue Context
The compliance checklist explicitly requires `compilerOptions.strict: true` and `compilerOptions.noImplicitAny: true`.

## Fix Focus Areas
- apps/activitypub/tsconfig.json[17-22]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. handleEnable lacks error handling 📘 Rule violation ⛯ Reliability
Description
handleEnable sets loading to true and awaits enableBlueskyMutation.mutateAsync() without
  any try/finally.
• If the mutation rejects, loading may never be reset, leaving the UI stuck in a loading state and
  providing no actionable feedback to users.
Code

apps/activitypub/src/views/Preferences/components/BlueskySharing.tsx[R50-55]

            setIsEditingProfile(true);
        } else {
            setLoading(true);
-            try {
-                await enableBlueskyMutation.mutateAsync();
-                toast.success('Bluesky sharing enabled');
-            } finally {
-                setLoading(false);
-            }
+            await enableBlueskyMutation.mutateAsync();
        }
    };
Evidence
Rule 3 requires robust handling of failure points with graceful degradation and actionable context.
The new code awaits an async mutation after setting loading but does not handle rejection or
guarantee resetting loading.

Rule 3: Generic: Robust Error Handling and Edge Case Management
apps/activitypub/src/views/Preferences/components/BlueskySharing.tsx[48-54]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`handleEnable` sets `loading=true` and awaits `enableBlueskyMutation.mutateAsync()` without handling errors or resetting state on failure.

## Issue Context
If the mutation rejects, React state may never be reset, leaving the UI stuck and violating robust error handling requirements.

## Fix Focus Areas
- apps/activitypub/src/views/Preferences/components/BlueskySharing.tsx[48-55]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Mutations swallow non-429 errors 📘 Rule violation ⛯ Reliability
Description
• The onError handlers for the Bluesky mutations only handle statusCode === 429 and do nothing
  for other errors.
• This creates silent failures with no feedback/logging/context, making production debugging and
  user recovery difficult.
Code

↗ apps/activitypub/src/hooks/use-activity-pub-queries.ts

            return api.disableBluesky();
Evidence
Rule 3 requires meaningful, contextual error handling for failure points. The mutation onError
callbacks ignore all errors except rate-limit responses, resulting in unhandled/hidden failures for
other status codes or network errors.

Rule 3: Generic: Robust Error Handling and Edge Case Management
apps/activitypub/src/hooks/use-activity-pub-queries.ts[2770-2774]
apps/activitypub/src/hooks/use-activity-pub-queries.ts[2802-2806]
apps/activitypub/src/hooks/use-activity-pub-queries.ts[2835-2839]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The Bluesky-related React Query mutations ignore all errors except HTTP 429, causing silent failures for other cases.

## Issue Context
Robust error handling requires that failures provide actionable context and graceful degradation rather than being dropped.

## Fix Focus Areas
- apps/activitypub/src/hooks/use-activity-pub-queries.ts[2770-2775]
- apps/activitypub/src/hooks/use-activity-pub-queries.ts[2802-2807]
- apps/activitypub/src/hooks/use-activity-pub-queries.ts[2835-2840]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (1)
4. Polling overlaps & unhandled rejections 🐞 Bug ⛯ Reliability
Description
• The confirmation loop uses setInterval but does not await the confirmation request, so a slow
  request can overlap with the next tick and create concurrent confirm calls.
• confirmHandle attaches only .then(...) to mutateAsync() with no .catch(...); any rejection
  becomes an unhandled promise rejection (especially likely when called repeatedly by a timer).
• The interval callback also awaits disableBlueskyMutation.mutateAsync() on timeout with no
  try/catch, which can also reject and leak an unhandled promise.
Code

apps/activitypub/src/views/Preferences/components/BlueskySharing.tsx[R68-117]

+    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
+
+    useEffect(() => {
+        if (!account?.blueskyEnabled) {
+            setHandleConfirmed(false);
+            setLoading(false);
+            retryCountRef.current = 0;
+
+            return;
+        }
+
+        if (account?.blueskyHandleConfirmed) {
+            setHandleConfirmed(true);
+            setLoading(false);
+
+            // Only show toast on first confirmation
+            if (retryCountRef.current > 0) {
+                toast.success('Bluesky sharing enabled');
+            }
+            retryCountRef.current = 0;
+
+            return;
+        }
+
+        setHandleConfirmed(false);
+        setLoading(true);
+        retryCountRef.current = 0;
+
+        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);
Evidence
The polling implementation triggers async work from a timer without awaiting or handling errors;
since the API layer throws on failures, the called mutateAsync() promises can reject and become
unhandled. Because the interval does not wait for completion, overlapping requests are possible when
responses take longer than CONFIRMATION_INTERVAL.

apps/activitypub/src/views/Preferences/components/BlueskySharing.tsx[68-75]
apps/activitypub/src/views/Preferences/components/BlueskySharing.tsx[103-117]
apps/activitypub/src/api/activitypub.ts[268-290]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The new confirmation polling uses a timer to trigger async work without awaiting completion and without catching rejections. This can cause overlapping confirmation requests and unhandled promise rejections.

### Issue Context
The API layer throws on non-2xx responses, so `mutateAsync()` can reject. Timer-driven code must always handle rejections.

### Fix Focus Areas
- apps/activitypub/src/views/Preferences/components/BlueskySharing.tsx[68-121]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



ⓘ The new review experience is currently in Beta. Learn more

Qodo Logo


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

Choose a reason for hiding this comment

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

Action required

1. tsconfig disables strict 📘 Rule violation ✓ Correctness

• The TypeScript config sets compilerOptions.strict to false, weakening compile-time guarantees
  and allowing unsafe typing patterns.
• This violates the requirement to enable strict typing (and, by extension, the associated
  noImplicitAny protections) for TypeScript configurations.
Agent prompt
## Issue description
`apps/activitypub/tsconfig.json` disables TypeScript strict mode, violating the requirement to keep `strict` (and `noImplicitAny`) enabled.

## Issue Context
The compliance checklist explicitly requires `compilerOptions.strict: true` and `compilerOptions.noImplicitAny: true`.

## Fix Focus Areas
- apps/activitypub/tsconfig.json[17-22]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines 50 to 55
setIsEditingProfile(true);
} else {
setLoading(true);
try {
await enableBlueskyMutation.mutateAsync();
toast.success('Bluesky sharing enabled');
} finally {
setLoading(false);
}
await enableBlueskyMutation.mutateAsync();
}
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

2. handleenable lacks error handling 📘 Rule violation ⛯ Reliability

handleEnable sets loading to true and awaits enableBlueskyMutation.mutateAsync() without
  any try/finally.
• If the mutation rejects, loading may never be reset, leaving the UI stuck in a loading state and
  providing no actionable feedback to users.
Agent prompt
## Issue description
`handleEnable` sets `loading=true` and awaits `enableBlueskyMutation.mutateAsync()` without handling errors or resetting state on failure.

## Issue Context
If the mutation rejects, React state may never be reset, leaving the UI stuck and violating robust error handling requirements.

## Fix Focus Areas
- apps/activitypub/src/views/Preferences/components/BlueskySharing.tsx[48-55]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +68 to +117
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

useEffect(() => {
if (!account?.blueskyEnabled) {
setHandleConfirmed(false);
setLoading(false);
retryCountRef.current = 0;

return;
}

if (account?.blueskyHandleConfirmed) {
setHandleConfirmed(true);
setLoading(false);

// Only show toast on first confirmation
if (retryCountRef.current > 0) {
toast.success('Bluesky sharing enabled');
}
retryCountRef.current = 0;

return;
}

setHandleConfirmed(false);
setLoading(true);
retryCountRef.current = 0;

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
Copy Markdown

Choose a reason for hiding this comment

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

Action required

4. Polling overlaps & unhandled rejections 🐞 Bug ⛯ Reliability

• The confirmation loop uses setInterval but does not await the confirmation request, so a slow
  request can overlap with the next tick and create concurrent confirm calls.
• confirmHandle attaches only .then(...) to mutateAsync() with no .catch(...); any rejection
  becomes an unhandled promise rejection (especially likely when called repeatedly by a timer).
• The interval callback also awaits disableBlueskyMutation.mutateAsync() on timeout with no
  try/catch, which can also reject and leak an unhandled promise.
Agent prompt
### Issue description
The new confirmation polling uses a timer to trigger async work without awaiting completion and without catching rejections. This can cause overlapping confirmation requests and unhandled promise rejections.

### Issue Context
The API layer throws on non-2xx responses, so `mutateAsync()` can reject. Timer-driven code must always handle rejections.

### Fix Focus Areas
- apps/activitypub/src/views/Preferences/components/BlueskySharing.tsx[68-121]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

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