chore(chat): migrate YouTube connector to Composio#1719
Conversation
- Add youtube to allowedArtistConnectors so the YouTube card appears in the artist settings Connectors tab alongside TikTok/Instagram - Rewrite useYoutubeChannel to call POST /api/connectors/actions with YOUTUBE_GET_CHANNEL_STATISTICS; consume the raw Google channels.list response (snippet/statistics/thumbnails) directly, no remapping - Rewrite useYoutubeStatus to derive connection state from the Composio connectors list instead of the legacy channel-info endpoint - Rewrite ConnectYouTubeButton + YoutubeLogoutButton to use the Composio authorize/disconnect flow already used by TikTok/Instagram - Drop legacy chat-side OAuth surface: /api/youtube/*, /api/auth/callback/google, lib/youtube OAuth/token helpers, lib/supabase/youtube_tokens, types/youtube.ts, useYouTubeLoginSuccess, the four LLM-tool result components for login/channels/playlist/thumb (those tools now come from Composio's YouTube toolkit) Keeps the get_youtube_revenue MCP tool dispatch in ToolComponents and the matching VercelChat result components — Composio has no YouTube Analytics action, so revenue stays custom on the api side.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Rate limit exceeded
To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughMigrates YouTube integration from bespoke OAuth/Supabase utilities to Composio connectors and Privy: removes local OAuth routes, token storage/validation/refresh utilities, and many YouTube UI components; adds connector-driven authorization, a connector action API helper, and refactored hooks/components to use connectors and runtime access tokens. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant UI as App UI
participant Connectors as Composio Connectors
participant API as executeConnectorActionApi
participant OAuth as OAuth Provider
User->>UI: Click "Connect YouTube"
UI->>Connectors: authorize(config) (useConnectors)
Connectors->>API: POST /connectors/actions (Bearer access token)
API->>OAuth: Request auth/redirect URL
OAuth-->>API: Return redirect URL
API-->>Connectors: Return URL
Connectors-->>UI: Provide redirect / open URL
User->>OAuth: Complete consent (provider)
OAuth-->>Connectors: Callback, persist connector state
Connectors-->>UI: Redirect back to app with connected state
UI->>API: executeConnectorActionApi (YOUTUBE_GET_CHANNEL_STATISTICS)
API->>Connectors: Request channel statistics (artistAccountId)
Connectors-->>API: Return channel items
API-->>UI: Channel statistics response
UI->>UI: Render channel info / update caches
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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. Review rate limit: 0/1 reviews remaining, refill in 58 minutes and 34 seconds.Comment |
There was a problem hiding this comment.
2 issues found across 48 files
Confidence score: 4/5
- This PR is likely safe to merge, with issues limited to repository structure/readability rules rather than functional or user-facing defects.
- The most severe finding is in
components/VercelChat/tools/youtube/YouTubeRevenueResult.tsx(4/10): the file exceeds the 100-line single-responsibility guideline, which increases maintenance risk but does not indicate a concrete runtime regression. components/YouTube/ChatInputYoutubeButtonPopover/PopoverContent.tsxalso exceeds the 100-line limit (3/10), reinforcing a mild maintainability concern rather than an immediate behavior break.- Pay close attention to
components/VercelChat/tools/youtube/YouTubeRevenueResult.tsx,components/YouTube/ChatInputYoutubeButtonPopover/PopoverContent.tsx- oversized modules may become harder to reason about and safely modify.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="components/YouTube/ChatInputYoutubeButtonPopover/PopoverContent.tsx">
<violation number="1" location="components/YouTube/ChatInputYoutubeButtonPopover/PopoverContent.tsx:101">
P3: Custom agent: **Code Structure and Size Limits for Readability and Single Responsibility**
This module exceeds the repository’s 100-line file limit.</violation>
</file>
<file name="components/VercelChat/tools/youtube/YouTubeRevenueResult.tsx">
<violation number="1" location="components/VercelChat/tools/youtube/YouTubeRevenueResult.tsx:10">
P2: Custom agent: **Code Structure and Size Limits for Readability and Single Responsibility**
File exceeds the 100-line limit required by the repository's readability/single-responsibility rule.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| @@ -1,26 +1,46 @@ | |||
| import React from 'react'; | |||
| import React from "react"; | |||
There was a problem hiding this comment.
P2: Custom agent: Code Structure and Size Limits for Readability and Single Responsibility
File exceeds the 100-line limit required by the repository's readability/single-responsibility rule.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At components/VercelChat/tools/youtube/YouTubeRevenueResult.tsx, line 10:
<comment>File exceeds the 100-line limit required by the repository's readability/single-responsibility rule.</comment>
<file context>
@@ -1,26 +1,46 @@
import YouTubeRevenueDaily from "./YouTubeRevenueDaily";
import { formatDate } from "@/lib/utils/formatDate";
+export interface YouTubeRevenueResult {
+ success: boolean;
+ status: string;
</file context>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1c9266b77b
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const ok = await disconnect(youtube.connectedAccountId); | ||
| if (ok) { | ||
| queryClient.invalidateQueries({ | ||
| queryKey: ["youtube-channel-info", artistAccountId], | ||
| }); |
There was a problem hiding this comment.
Refresh connector status after YouTube disconnect
YoutubeLogoutButton disconnects using its own useConnectors instance and then only invalidates the youtube-channel-info React Query key. The connected/disconnected UI in StandaloneYoutubeComponent is driven by useYoutubeStatus, which uses a separate local useConnectors state (not that query key), so it does not update after logout and can keep showing YouTube as connected until the modal/page remounts. This reproduces in the artist settings flow where both components are siblings (components/ArtistSetting/Inputs.tsx).
Useful? React with 👍 / 👎.
…TICS Composio's action defaults the `part` parameter to "statistics" only, so the response had `snippet: null` and the UI rendered without title or thumbnail. Pass `part: "snippet,statistics"` so the channel name, description, and thumbnail come back too.
There was a problem hiding this comment.
1 issue found across 48 files
Confidence score: 4/5
- This PR is likely safe to merge with minimal risk: the reported issue is moderate-low severity (4/10) and focused on one interaction path.
- In
components/ArtistSetting/StandaloneYoutubeComponent/YoutubeLogoutButton.tsx, the disconnect action can be triggered multiple times while a request is still in flight, which may cause duplicate logout/disconnect requests for fast repeat clicks. - The impact appears user-facing but contained (request duplication rather than core flow breakage), so this is a polish/stability fix rather than a merge blocker.
- Pay close attention to
components/ArtistSetting/StandaloneYoutubeComponent/YoutubeLogoutButton.tsx- prevent repeated clicks during pending disconnect to avoid duplicate requests.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="components/ArtistSetting/StandaloneYoutubeComponent/YoutubeLogoutButton.tsx">
<violation number="1" location="components/ArtistSetting/StandaloneYoutubeComponent/YoutubeLogoutButton.tsx:49">
P2: The logout button is clickable during an in-flight disconnect, so users can fire duplicate disconnect requests by clicking rapidly.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
Actionable comments posted: 8
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@components/ArtistSetting/StandaloneYoutubeComponent/ChannelInfo.tsx`:
- Line 22: The caption for the YouTube section is rendered with a form <label>
element in ChannelInfo.tsx (the element using className cn("text-sm", { hidden:
dense })), which is semantically incorrect for static UI; replace that <label>
with a non-form text element such as a <p> or <span> (keeping the same className
and conditional hidden logic) so the visual styling and hide behavior remain
unchanged but assistive technology receives correct semantics for static text.
In `@components/ArtistSetting/StandaloneYoutubeComponent/YoutubeLogoutButton.tsx`:
- Around line 46-52: The icon-only disconnect button lacks an accessible name;
update the Button (the component rendering the XIcon and using
onClick={handleClick}) to include an accessible label—e.g., add
aria-label="Disconnect YouTube" or aria-label="Logout" (or include an offscreen
span with visually-hidden text) so screen readers announce the button purpose
while keeping the XIcon for visual users.
- Around line 33-50: The click handler handleClick can fire multiple times while
disconnect() is in flight; add a local boolean state (e.g., isDisconnecting) in
the YoutubeLogoutButton component, set it true before awaiting
disconnect(youtube.connectedAccountId) and false after, and guard early returns
with it so duplicate requests are prevented; also pass isDisconnecting to the
Button (disabled prop or conditional class) so the UI is disabled during the
in-flight request and still call
queryClient.invalidateQueries(["youtube-channel-info", artistAccountId]) only
when the disconnect completes successfully.
In `@components/common/ConnectYouTubeButton.tsx`:
- Around line 52-65: The ConnectYouTubeButton component currently hides its
visible label when the dense prop is true, leaving the button unlabeled for
assistive tech; update the Button rendered in ConnectYouTubeButton to provide an
accessible name when dense is true by adding an aria-label (e.g., "Connect
YouTube Account") or including a visually hidden element (sr-only) with the same
text that is conditionally rendered when dense is true, ensuring the existing
visible text remains for non-dense mode and that the aria-label/text matches the
visible label for consistency.
In `@components/VercelChat/tools/youtube/YouTubeRevenueResult.tsx`:
- Around line 33-39: YouTubeRevenueResult currently passes result.message (which
is optional) directly to YouTubeRevenueError and can render an empty message;
update the error branch in YouTubeRevenueResult to pass a fallback user-facing
string (e.g. "An error occurred while fetching revenue.") when result.message is
undefined or empty so YouTubeRevenueError always receives a non-empty message;
locate the conditional in YouTubeRevenueResult and replace the direct
result.message usage with the fallback.
In `@components/YouTube/ChatInputYoutubeButtonPopover/index.tsx`:
- Around line 37-44: The trigger currently uses a non-semantic <div> with
onClick to toggle isOpen, which prevents keyboard activation; replace that
clickable div with a semantic, focusable trigger (preferably a <button>) or, if
children are already interactive, render a separate dedicated trigger element
wrapping children so you don't nest interactive controls; ensure the toggle
logic uses setIsOpen(!isOpen), add keyboard handlers to activate on Enter/Space
(or rely on native button behavior), and keep aria-expanded/aria-controls or
appropriate roles in sync with isOpen to support accessibility.
In `@components/YouTube/ChatInputYoutubeButtonPopover/PopoverContent.tsx`:
- Around line 35-43: The img tag in PopoverContent.tsx uses
thumbnails?.high?.url || thumbnails?.medium?.url || thumbnails?.default?.url ||
"" which can produce an empty src; change the rendering to only output the <img>
when a non-empty URL exists (check the computed thumbnailUrl variable derived
from thumbnails), and otherwise render a fallback (e.g., an avatar
placeholder/div or an accessible aria-label using title) so no <img> is created
with an empty src; update the JSX around the img (same className used currently)
to conditionally render based on thumbnailUrl and keep title/alt behavior.
In `@hooks/useYoutubeStatus.ts`:
- Line 38: The return value currently hard-codes error: null which hides real
errors from useConnectors; update the hook (hooks/useYoutubeStatus.ts) to
propagate the actual error from the connector call instead of null—use the error
variable returned by useConnectors (or the connector error name in this file)
and return { data, isLoading, error } so consumers can detect failures
consistently.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: ec06b559-55a2-4735-9ee0-790169f55f78
⛔ Files ignored due to path filters (1)
types/youtube.tsis excluded by none and included by none
📒 Files selected for processing (47)
app/api/auth/callback/google/route.tsapp/api/youtube/channel-info/route.tsapp/api/youtube/logout/route.tscomponents/ArtistSetting/StandaloneYoutubeComponent/ChannelInfo.tsxcomponents/ArtistSetting/StandaloneYoutubeComponent/YoutubeLogoutButton.tsxcomponents/VercelChat/ToolComponents.tsxcomponents/VercelChat/YoutubeVideoStats.tsxcomponents/VercelChat/dialogs/YoutubeVideoDialog.tsxcomponents/VercelChat/tools/youtube/YouTubeAccessSkeleton.tsxcomponents/VercelChat/tools/youtube/YouTubeChannelDisplay.tsxcomponents/VercelChat/tools/youtube/YouTubeChannelVideosListSkeleton.tsxcomponents/VercelChat/tools/youtube/YouTubeChannelsResult.tsxcomponents/VercelChat/tools/youtube/YouTubeErrorDisplay.tsxcomponents/VercelChat/tools/youtube/YouTubeLoginResult.tsxcomponents/VercelChat/tools/youtube/YouTubeRevenueResult.tsxcomponents/VercelChat/tools/youtube/YouTubeSetThumbnailResult.tsxcomponents/VercelChat/tools/youtube/YouTubeSetThumbnailSkeleton.tsxcomponents/VercelChat/tools/youtube/YoutubeChannelVideosListResult.tsxcomponents/VercelChat/tools/youtube/YoutubeVideoCard.tsxcomponents/YouTube/ChatInputYoutubeButtonPopover/PopoverContent.tsxcomponents/YouTube/ChatInputYoutubeButtonPopover/index.tsxcomponents/common/ConnectYouTubeButton.tsxhooks/useArtistConnectorCallback.tshooks/useYouTubeLoginSuccess.tshooks/useYoutubeChannel.tsxhooks/useYoutubeStatus.tslib/composio/allowedArtistConnectors.tslib/composio/api/executeConnectorActionApi.tslib/supabase/youtube_tokens/deleteYouTubeTokens.tslib/supabase/youtube_tokens/getYouTubeTokens.tslib/supabase/youtube_tokens/insertYouTubeTokens.tslib/youtube/authenticated-channel-monetization.tslib/youtube/channel-fetcher.tslib/youtube/channel-monetization-by-id.tslib/youtube/error-builder.tslib/youtube/fetchYouTubeChannel.tslib/youtube/formatDuration.tslib/youtube/getResizedImageBuffer.tslib/youtube/getYoutubePlaylistVideos.tslib/youtube/is-token-expired.tslib/youtube/oauth-client.tslib/youtube/queryAnalyticsReports.tslib/youtube/revenue-error-handler.tslib/youtube/token-refresher.tslib/youtube/token-validator.tslib/youtube/youtube-analytics-oauth-client.tslib/youtube/youtubeLogin.ts
💤 Files with no reviewable changes (35)
- components/VercelChat/tools/youtube/YouTubeChannelVideosListSkeleton.tsx
- lib/youtube/youtube-analytics-oauth-client.ts
- components/VercelChat/tools/youtube/YouTubeSetThumbnailSkeleton.tsx
- lib/youtube/is-token-expired.ts
- app/api/youtube/logout/route.ts
- lib/youtube/formatDuration.ts
- lib/supabase/youtube_tokens/deleteYouTubeTokens.ts
- components/VercelChat/tools/youtube/YouTubeLoginResult.tsx
- components/VercelChat/tools/youtube/YouTubeAccessSkeleton.tsx
- hooks/useYouTubeLoginSuccess.ts
- components/VercelChat/tools/youtube/YoutubeChannelVideosListResult.tsx
- components/VercelChat/tools/youtube/YouTubeChannelDisplay.tsx
- lib/supabase/youtube_tokens/insertYouTubeTokens.ts
- components/VercelChat/tools/youtube/YouTubeSetThumbnailResult.tsx
- components/VercelChat/YoutubeVideoStats.tsx
- lib/supabase/youtube_tokens/getYouTubeTokens.ts
- lib/youtube/youtubeLogin.ts
- lib/youtube/getYoutubePlaylistVideos.ts
- lib/youtube/error-builder.ts
- lib/youtube/getResizedImageBuffer.ts
- app/api/youtube/channel-info/route.ts
- lib/youtube/oauth-client.ts
- components/VercelChat/tools/youtube/YouTubeChannelsResult.tsx
- lib/youtube/revenue-error-handler.ts
- lib/youtube/queryAnalyticsReports.ts
- lib/youtube/channel-fetcher.ts
- lib/youtube/token-refresher.ts
- app/api/auth/callback/google/route.ts
- lib/youtube/channel-monetization-by-id.ts
- lib/youtube/token-validator.ts
- components/VercelChat/tools/youtube/YouTubeErrorDisplay.tsx
- lib/youtube/authenticated-channel-monetization.ts
- components/VercelChat/tools/youtube/YoutubeVideoCard.tsx
- lib/youtube/fetchYouTubeChannel.ts
- components/VercelChat/dialogs/YoutubeVideoDialog.tsx
Both components were orphaned — ChatInputYoutubeButton had zero callers, which made ChatInputYoutubeButtonPopover and its StatCard / PopoverContent unreachable too. Drop them entirely.
api now passes Composio's ToolExecuteResponse through unchanged
({successful, data, error}). Consumer adapter unwraps once so all
chat-side callers get the underlying provider payload directly
(e.g. youtube.channels.list with `items`); upstream failures throw.
- Share connector refresh across useConnectors instances so logout updates sibling status (P1: useYoutubeStatus stale after disconnect) - Disable disconnect button while in-flight to prevent duplicate requests - Add aria-label to icon-only disconnect button - Add aria-label to ConnectYouTubeButton for dense (icon-only) mode - Propagate connector errors from useYoutubeStatus instead of hard-coded null - Use semantic <span> for static section caption in ChannelInfo - Provide fallback message in YouTubeRevenueResult when error text missing Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
1 issue found across 6 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="hooks/useConnectors.ts">
<violation number="1" location="hooks/useConnectors.ts:9">
P2: Custom agent: **Enforce Clear Code Style and Maintainability Practices**
File exceeds the 100-line limit required by the maintainability rule.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
The api endpoint always returns Composio's ToolExecuteResponse envelope
(`{ successful, data, error }`) — every Composio Vercel-AI-SDK tool returns
that shape. Drop the defensive non-envelope branch and read the typed shape
directly, removing the `as unknown` casts and inline type guards.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…hannel - useConnectors now uses React Query so all instances share one cache; disconnect/authorize invalidate the connectors key and sibling instances re-render automatically. Drops the module-level Set<()=>void> broadcast. - Extract fetchYoutubeChannel + types to lib/youtube/fetchYoutubeChannel.ts, matching the fetchConnectorsApi / authorizeConnectorApi pattern. The hook is now ~20 lines and just wires React Query to the helper. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
fetchConnectorsApi already returns Promise<ConnectorInfo[]>, so TS infers the queryFn type correctly without the explicit annotation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@hooks/useYoutubeChannel.tsx`:
- Around line 9-19: The query in useYoutubeChannel (the useQuery call) is
currently only gated by artistAccountId so it can run unauthenticated; update
the hook to also read the authenticated flag from usePrivy (alongside
getAccessToken) and change the enabled option to `!!artistAccountId &&
authenticated` so the query won't execute when `getAccessToken()` would return
null; keep the fetchYoutubeChannel call and the existing error handling intact.
In `@lib/youtube/fetchYoutubeChannel.ts`:
- Around line 35-56: fetchYoutubeChannel currently returns a possibly-empty
YouTubeChannelStatisticsResponse and lets undefined/empty items propagate;
update fetchYoutubeChannel to validate the response from
executeConnectorActionApi (the call inside fetchYoutubeChannel) and throw a
clear error if result.items is missing or an empty array (e.g., after awaiting
executeConnectorActionApi<YouTubeChannelStatisticsResponse>(...), check
response.items && response.items.length > 0 and throw a descriptive Error like
"YouTube channel not found for account {artistAccountId}" when the check fails)
so callers get a fast, explicit failure instead of silently handling undefined
data.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 6e616f37-6470-491d-90ad-b59c7221b677
📒 Files selected for processing (9)
components/ArtistSetting/StandaloneYoutubeComponent/ChannelInfo.tsxcomponents/ArtistSetting/StandaloneYoutubeComponent/YoutubeLogoutButton.tsxcomponents/VercelChat/tools/youtube/YouTubeRevenueResult.tsxcomponents/common/ConnectYouTubeButton.tsxhooks/useConnectors.tshooks/useYoutubeChannel.tsxhooks/useYoutubeStatus.tslib/composio/api/executeConnectorActionApi.tslib/youtube/fetchYoutubeChannel.ts
🚧 Files skipped from review as they are similar to previous changes (5)
- hooks/useYoutubeStatus.ts
- components/ArtistSetting/StandaloneYoutubeComponent/ChannelInfo.tsx
- components/common/ConnectYouTubeButton.tsx
- components/VercelChat/tools/youtube/YouTubeRevenueResult.tsx
- components/ArtistSetting/StandaloneYoutubeComponent/YoutubeLogoutButton.tsx
There was a problem hiding this comment.
1 issue found across 4 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="hooks/useConnectors.ts">
<violation number="1" location="hooks/useConnectors.ts:78">
P2: `authorize` no longer handles API failures, so it can reject instead of returning `null` as the hook contract implies.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
…ntract Dropped during the React Query refactor — without the catch, an API failure rejects the promise instead of returning null, which the only caller (ConnectYouTubeButton) doesn't handle. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Matches the codebase convention (useTaskRunStatus, useCatalogs, useArtistPosts, etc.) — without it, the query runs and immediately throws "Not authenticated" when the user isn't signed in. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="hooks/useConnectors.ts">
<violation number="1" location="hooks/useConnectors.ts:78">
P2: Custom agent: **Code Structure and Size Limits for Readability and Single Responsibility**
File exceeds the 100-line limit required for readability and single-responsibility.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| @@ -1,6 +1,7 @@ | |||
| "use client"; | |||
There was a problem hiding this comment.
P2: Custom agent: Code Structure and Size Limits for Readability and Single Responsibility
File exceeds the 100-line limit required for readability and single-responsibility.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At hooks/useConnectors.ts, line 78:
<comment>File exceeds the 100-line limit required for readability and single-responsibility.</comment>
<file context>
@@ -75,11 +75,15 @@ export function useConnectors(config?: UseConnectorsConfig) {
- accountId,
- callbackUrl,
- });
+ try {
+ return await authorizeConnectorApi(accessToken, {
+ connector,
</file context>
| } finally { | ||
| setIsDisconnecting(false); | ||
| } | ||
| }; |
There was a problem hiding this comment.
DRY - is there an existing, shared, hook for managing disconnection of a connector?
| const handleClick = async () => { | ||
| if (!accountId || isConnecting) return; | ||
| setIsConnecting(true); | ||
| try { | ||
| const redirectUrl = await authorize("youtube"); | ||
| if (redirectUrl) { | ||
| window.location.href = redirectUrl; | ||
| } | ||
| } finally { | ||
| setIsConnecting(false); | ||
| } | ||
| }; |
There was a problem hiding this comment.
DRY - is there an existing hook for connecting a connector?
Address sweetman's DRY review feedback on PR #1719: both ConnectYouTubeButton and YoutubeLogoutButton open-coded the connect/disconnect state machine that useConnectorHandlers (and ConnectorCard) already provide. Add an optional onDisconnectSuccess callback to the hook so the logout button can still invalidate the youtube-channel-info query after a successful disconnect.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@components/common/ConnectYouTubeButton.tsx`:
- Around line 29-32: The callbackUrl construction in ConnectYouTubeButton uses
manual string interpolation for query params (callbackUrl, accountId) which can
produce malformed URLs; update the callbackUrl logic in the ConnectYouTubeButton
component to build query parameters with URLSearchParams (or new URL) and append
artist_connected=true and artist_id set to accountId (or empty string) so
artist_id is properly URL-encoded before concatenation; keep the window
origin/pathname usage but replace the template string assembly with
URLSearchParams (or URL) to generate the final URL.
In `@hooks/useConnectors.ts`:
- Around line 60-63: The connector filtering builds allowed = new
Set(slugFilter) but compares against c.slug.toLowerCase(), so mixed-case inputs
in slugFilter (e.g., "YouTube") will fail; update the useMemo in
useConnectors.ts where connectors is computed to normalize slugFilter items to
lower case when building the Set (e.g., map each entry to .toLowerCase() and
then new Set(...)) so membership checks against c.slug.toLowerCase() succeed;
keep the same dependencies [query.data, slugFilter].
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: ceec2eff-2b68-4553-b1ec-008dbc29a11e
📒 Files selected for processing (5)
components/ArtistSetting/StandaloneYoutubeComponent/YoutubeLogoutButton.tsxcomponents/common/ConnectYouTubeButton.tsxhooks/useConnectorHandlers.tshooks/useConnectors.tshooks/useYoutubeChannel.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- hooks/useYoutubeChannel.tsx
| callbackUrl: | ||
| typeof window !== "undefined" | ||
| ? `${window.location.origin}${window.location.pathname}?artist_connected=true&artist_id=${accountId ?? ""}` | ||
| : undefined, |
There was a problem hiding this comment.
Build callbackUrl with URLSearchParams instead of manual string concat.
At Line 31, artist_id is interpolated directly into the query string. If accountId contains reserved characters, the callback URL can be malformed.
Proposed fix
callbackUrl:
typeof window !== "undefined"
- ? `${window.location.origin}${window.location.pathname}?artist_connected=true&artist_id=${accountId ?? ""}`
+ ? (() => {
+ const url = new URL(
+ window.location.pathname,
+ window.location.origin,
+ );
+ url.searchParams.set("artist_connected", "true");
+ url.searchParams.set("artist_id", accountId ?? "");
+ return url.toString();
+ })()
: undefined,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/common/ConnectYouTubeButton.tsx` around lines 29 - 32, The
callbackUrl construction in ConnectYouTubeButton uses manual string
interpolation for query params (callbackUrl, accountId) which can produce
malformed URLs; update the callbackUrl logic in the ConnectYouTubeButton
component to build query parameters with URLSearchParams (or new URL) and append
artist_connected=true and artist_id set to accountId (or empty string) so
artist_id is properly URL-encoded before concatenation; keep the window
origin/pathname usage but replace the template string assembly with
URLSearchParams (or URL) to generate the final URL.
| const connectors = useMemo(() => { | ||
| const allowed = new Set(slugFilter); | ||
| return (query.data ?? []).filter((c) => allowed.has(c.slug.toLowerCase())); | ||
| }, [query.data, slugFilter]); |
There was a problem hiding this comment.
Normalize allowedSlugs before membership checks.
At Line 61, allowedSlugs are added as-is, while connector slugs are lowercased at Line 62. Mixed-case inputs (e.g., "YouTube") will be incorrectly filtered out.
Proposed fix
const connectors = useMemo(() => {
- const allowed = new Set(slugFilter);
+ const allowed = new Set(slugFilter.map((slug) => slug.toLowerCase()));
return (query.data ?? []).filter((c) => allowed.has(c.slug.toLowerCase()));
}, [query.data, slugFilter]);📝 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 connectors = useMemo(() => { | |
| const allowed = new Set(slugFilter); | |
| return (query.data ?? []).filter((c) => allowed.has(c.slug.toLowerCase())); | |
| }, [query.data, slugFilter]); | |
| const connectors = useMemo(() => { | |
| const allowed = new Set(slugFilter.map((slug) => slug.toLowerCase())); | |
| return (query.data ?? []).filter((c) => allowed.has(c.slug.toLowerCase())); | |
| }, [query.data, slugFilter]); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@hooks/useConnectors.ts` around lines 60 - 63, The connector filtering builds
allowed = new Set(slugFilter) but compares against c.slug.toLowerCase(), so
mixed-case inputs in slugFilter (e.g., "YouTube") will fail; update the useMemo
in useConnectors.ts where connectors is computed to normalize slugFilter items
to lower case when building the Set (e.g., map each entry to .toLowerCase() and
then new Set(...)) so membership checks against c.slug.toLowerCase() succeed;
keep the same dependencies [query.data, slugFilter].
Why: the formatConnectorName fallback regex inserts a space before each capital, turning the API's "YouTube" into "You Tube". Add youtube to the explicit display-name map alongside the other multi-cap brands. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Without an explicit metadata entry, the YouTube card showed the generic "Connect to enable this connector" placeholder. Match the TikTok/Instagram pattern. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
You're iterating quickly on this pull request. To help protect your rate limits, cubic has paused automatic reviews on new pushes for now—when you're ready for another review, comment |
* chore(chat): migrate YouTube connector to Composio
- Add youtube to allowedArtistConnectors so the YouTube card appears in
the artist settings Connectors tab alongside TikTok/Instagram
- Rewrite useYoutubeChannel to call POST /api/connectors/actions with
YOUTUBE_GET_CHANNEL_STATISTICS; consume the raw Google channels.list
response (snippet/statistics/thumbnails) directly, no remapping
- Rewrite useYoutubeStatus to derive connection state from the Composio
connectors list instead of the legacy channel-info endpoint
- Rewrite ConnectYouTubeButton + YoutubeLogoutButton to use the Composio
authorize/disconnect flow already used by TikTok/Instagram
- Drop legacy chat-side OAuth surface: /api/youtube/*,
/api/auth/callback/google, lib/youtube OAuth/token helpers,
lib/supabase/youtube_tokens, types/youtube.ts, useYouTubeLoginSuccess,
the four LLM-tool result components for login/channels/playlist/thumb
(those tools now come from Composio's YouTube toolkit)
Keeps the get_youtube_revenue MCP tool dispatch in ToolComponents and the
matching VercelChat result components — Composio has no YouTube
Analytics action, so revenue stays custom on the api side.
* fix(chat): request snippet+statistics from YOUTUBE_GET_CHANNEL_STATISTICS
Composio's action defaults the `part` parameter to "statistics" only,
so the response had `snippet: null` and the UI rendered without title
or thumbnail. Pass `part: "snippet,statistics"` so the channel name,
description, and thumbnail come back too.
* chore(chat): remove unused ChatInputYoutubeButton + popover
Both components were orphaned — ChatInputYoutubeButton had zero callers,
which made ChatInputYoutubeButtonPopover and its StatCard / PopoverContent
unreachable too. Drop them entirely.
* fix(chat): unwrap Composio envelope in executeConnectorActionApi
api now passes Composio's ToolExecuteResponse through unchanged
({successful, data, error}). Consumer adapter unwraps once so all
chat-side callers get the underlying provider payload directly
(e.g. youtube.channels.list with `items`); upstream failures throw.
* fix(chat): address AI review feedback on youtube migration
- Share connector refresh across useConnectors instances so logout updates
sibling status (P1: useYoutubeStatus stale after disconnect)
- Disable disconnect button while in-flight to prevent duplicate requests
- Add aria-label to icon-only disconnect button
- Add aria-label to ConnectYouTubeButton for dense (icon-only) mode
- Propagate connector errors from useYoutubeStatus instead of hard-coded null
- Use semantic <span> for static section caption in ChannelInfo
- Provide fallback message in YouTubeRevenueResult when error text missing
* refactor(chat): simplify executeConnectorActionApi envelope handling
The api endpoint always returns Composio's ToolExecuteResponse envelope
(`{ successful, data, error }`) — every Composio Vercel-AI-SDK tool returns
that shape. Drop the defensive non-envelope branch and read the typed shape
directly, removing the `as unknown` casts and inline type guards.
* refactor(chat): React Query for useConnectors + extract fetchYoutubeChannel
- useConnectors now uses React Query so all instances share one cache;
disconnect/authorize invalidate the connectors key and sibling instances
re-render automatically. Drops the module-level Set<()=>void> broadcast.
- Extract fetchYoutubeChannel + types to lib/youtube/fetchYoutubeChannel.ts,
matching the fetchConnectorsApi / authorizeConnectorApi pattern. The hook
is now ~20 lines and just wires React Query to the helper.
* chore(chat): drop redundant queryFn return-type annotation
fetchConnectorsApi already returns Promise<ConnectorInfo[]>, so TS infers
the queryFn type correctly without the explicit annotation.
* fix(chat): restore authorize catch to honor Promise<string | null> contract
Dropped during the React Query refactor — without the catch, an API failure
rejects the promise instead of returning null, which the only caller
(ConnectYouTubeButton) doesn't handle.
* fix(chat): gate useYoutubeChannel on Privy authenticated flag
Matches the codebase convention (useTaskRunStatus, useCatalogs, useArtistPosts,
etc.) — without it, the query runs and immediately throws "Not authenticated"
when the user isn't signed in.
* refactor(chat): use shared useConnectorHandlers in YouTube buttons
Address sweetman's DRY review feedback on PR #1719: both ConnectYouTubeButton
and YoutubeLogoutButton open-coded the connect/disconnect state machine that
useConnectorHandlers (and ConnectorCard) already provide. Add an optional
onDisconnectSuccess callback to the hook so the logout button can still
invalidate the youtube-channel-info query after a successful disconnect.
* fix(chat): display "YouTube" as one word in connectors UI
Why: the formatConnectorName fallback regex inserts a space before
each capital, turning the API's "YouTube" into "You Tube". Add
youtube to the explicit display-name map alongside the other
multi-cap brands.
* fix(chat): add YouTube connector description
Without an explicit metadata entry, the YouTube card showed the
generic "Connect to enable this connector" placeholder. Match the
TikTok/Instagram pattern.
---------
Co-authored-by: Arpit Gupta <arpitgupta1214@gmail.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Also, |
* chore(chat): migrate YouTube connector to Composio (#1719) * chore(chat): migrate YouTube connector to Composio - Add youtube to allowedArtistConnectors so the YouTube card appears in the artist settings Connectors tab alongside TikTok/Instagram - Rewrite useYoutubeChannel to call POST /api/connectors/actions with YOUTUBE_GET_CHANNEL_STATISTICS; consume the raw Google channels.list response (snippet/statistics/thumbnails) directly, no remapping - Rewrite useYoutubeStatus to derive connection state from the Composio connectors list instead of the legacy channel-info endpoint - Rewrite ConnectYouTubeButton + YoutubeLogoutButton to use the Composio authorize/disconnect flow already used by TikTok/Instagram - Drop legacy chat-side OAuth surface: /api/youtube/*, /api/auth/callback/google, lib/youtube OAuth/token helpers, lib/supabase/youtube_tokens, types/youtube.ts, useYouTubeLoginSuccess, the four LLM-tool result components for login/channels/playlist/thumb (those tools now come from Composio's YouTube toolkit) Keeps the get_youtube_revenue MCP tool dispatch in ToolComponents and the matching VercelChat result components — Composio has no YouTube Analytics action, so revenue stays custom on the api side. * fix(chat): request snippet+statistics from YOUTUBE_GET_CHANNEL_STATISTICS Composio's action defaults the `part` parameter to "statistics" only, so the response had `snippet: null` and the UI rendered without title or thumbnail. Pass `part: "snippet,statistics"` so the channel name, description, and thumbnail come back too. * chore(chat): remove unused ChatInputYoutubeButton + popover Both components were orphaned — ChatInputYoutubeButton had zero callers, which made ChatInputYoutubeButtonPopover and its StatCard / PopoverContent unreachable too. Drop them entirely. * fix(chat): unwrap Composio envelope in executeConnectorActionApi api now passes Composio's ToolExecuteResponse through unchanged ({successful, data, error}). Consumer adapter unwraps once so all chat-side callers get the underlying provider payload directly (e.g. youtube.channels.list with `items`); upstream failures throw. * fix(chat): address AI review feedback on youtube migration - Share connector refresh across useConnectors instances so logout updates sibling status (P1: useYoutubeStatus stale after disconnect) - Disable disconnect button while in-flight to prevent duplicate requests - Add aria-label to icon-only disconnect button - Add aria-label to ConnectYouTubeButton for dense (icon-only) mode - Propagate connector errors from useYoutubeStatus instead of hard-coded null - Use semantic <span> for static section caption in ChannelInfo - Provide fallback message in YouTubeRevenueResult when error text missing Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(chat): simplify executeConnectorActionApi envelope handling The api endpoint always returns Composio's ToolExecuteResponse envelope (`{ successful, data, error }`) — every Composio Vercel-AI-SDK tool returns that shape. Drop the defensive non-envelope branch and read the typed shape directly, removing the `as unknown` casts and inline type guards. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(chat): React Query for useConnectors + extract fetchYoutubeChannel - useConnectors now uses React Query so all instances share one cache; disconnect/authorize invalidate the connectors key and sibling instances re-render automatically. Drops the module-level Set<()=>void> broadcast. - Extract fetchYoutubeChannel + types to lib/youtube/fetchYoutubeChannel.ts, matching the fetchConnectorsApi / authorizeConnectorApi pattern. The hook is now ~20 lines and just wires React Query to the helper. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(chat): drop redundant queryFn return-type annotation fetchConnectorsApi already returns Promise<ConnectorInfo[]>, so TS infers the queryFn type correctly without the explicit annotation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(chat): restore authorize catch to honor Promise<string | null> contract Dropped during the React Query refactor — without the catch, an API failure rejects the promise instead of returning null, which the only caller (ConnectYouTubeButton) doesn't handle. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(chat): gate useYoutubeChannel on Privy authenticated flag Matches the codebase convention (useTaskRunStatus, useCatalogs, useArtistPosts, etc.) — without it, the query runs and immediately throws "Not authenticated" when the user isn't signed in. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(chat): use shared useConnectorHandlers in YouTube buttons Address sweetman's DRY review feedback on PR #1719: both ConnectYouTubeButton and YoutubeLogoutButton open-coded the connect/disconnect state machine that useConnectorHandlers (and ConnectorCard) already provide. Add an optional onDisconnectSuccess callback to the hook so the logout button can still invalidate the youtube-channel-info query after a successful disconnect. * fix(chat): display "YouTube" as one word in connectors UI Why: the formatConnectorName fallback regex inserts a space before each capital, turning the API's "YouTube" into "You Tube". Add youtube to the explicit display-name map alongside the other multi-cap brands. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(chat): add YouTube connector description Without an explicit metadata entry, the YouTube card showed the generic "Connect to enable this connector" placeholder. Match the TikTok/Instagram pattern. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Sweets Sweetman <sweetmantech@gmail.com> * Remove deprecated Stripe session handling and refactor checkout session creation to use access tokens. Update subscription handling in the useSubscribeClick hook to ensure proper authentication flow. (#1728) --------- Co-authored-by: Arpit Gupta <arpitgupta1214@gmail.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: ahmednahima0-beep <ahmednahima0@gmail.com>
Summary
Replaces the chat-side custom YouTube OAuth flow with the existing Composio connector path. Connect/disconnect/channel-info all go through `/api/connectors` and `/api/connectors/actions`. The legacy `/api/youtube/*` routes, the `youtube_tokens` Supabase helpers, and the four LLM-tool result components for login/channels/playlist/thumbnail are deleted (Composio's toolkit covers them). Revenue dispatch is kept since the api-side `get_youtube_revenue` MCP tool stays.
Pairs with api#494 and docs#XXX.
Test plan
Summary by cubic
Migrates YouTube to Composio for auth and channel stats, removes legacy OAuth/UI, unwraps Composio responses, and moves connector state to React Query; keeps the custom revenue tool. Adds proper “YouTube” naming and a connector description so the settings card reads correctly.
Refactors
ALLOWED_ARTIST_CONNECTORS; connect/disconnect now useuseConnectors+useConnectorHandlers(shared React Query cache, in-flight disable, aria-labels). AddsonDisconnectSuccessto also invalidate["youtube-channel-info", accountId]and restoresauthorizeto returnnullon failure.useYoutubeChannelcallsYOUTUBE_GET_CHANNEL_STATISTICSviaexecuteConnectorActionApiwithpart: "snippet,statistics"; extractslib/youtube/fetchYoutubeChannel; query gated on Privyauthenticated.useYoutubeStatusnow derives from connectors and surfaces errors.lib/composio/api/executeConnectorActionApito unwrap Composio’sToolExecuteResponse. Mapsyoutubeto “YouTube” informatConnectorNameand adds a YouTube description inconnectorMetadata./api/auth/callback/google,/api/youtube/*, Supabaseyoutube_tokens, OAuth/analytics libs,types/youtube.ts, and unused chat UIs; keepsget_youtube_revenueMCP tool/UI with an inlined result type and fallback error message.Migration
api#494(Composio actions endpoint). Ensure the Composio YouTube connector is enabled; users must reconnect via settings. Legacy Supabaseyoutube_tokensare no longer used and can be cleaned up separately.Written for commit 8cfe958. Summary will update on new commits.
Summary by CodeRabbit
Refactor
Bug Fixes / Chores