feat: show social PFP and username on connected artist connectors#1644
feat: show social PFP and username on connected artist connectors#1644recoup-coding-agent wants to merge 1 commit intotestfrom
Conversation
When an artist has a connected Instagram or TikTok connector, the connector card now displays the social profile picture and @username instead of the generic connector icon and description. Co-Authored-By: Paperclip <noreply@paperclip.ing>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughThis PR integrates artist social media metadata into the connectors interface. A new matching utility maps connector slugs to social profiles from fetched data, which ArtistConnectorsTab retrieves and passes to ConnectorCard, enabling social avatars and usernames to display alongside connector information. Changes
Sequence DiagramsequenceDiagram
participant AT as ArtistConnectorsTab
participant API as Query API
participant M as matchSocialToConnector
participant CC as ConnectorCard
participant UI as UI Render
AT->>API: useQuery(getArtistSocials, {artistAccountId})
API-->>AT: socials[] data
AT->>AT: For each connector
AT->>M: matchSocialToConnector(slug, socials)
M->>M: Map slug to domain
M->>M: Search socials for matching URL
M-->>AT: matching Social | undefined
AT->>CC: Pass socialAvatar & socialUsername props
CC->>CC: Compute showSocialProfile
CC->>CC: Conditionally render avatar & username
CC-->>UI: Social profile display or fallback
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes The changes introduce new data-fetching logic, cross-component data passing, and a utility function that requires understanding the matching pattern. The scope spans three files with coordinated responsibility, but the implementation follows a clear, focused pattern without excessive complexity. Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ❌ 1❌ Failed checks (1 warning)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 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. Comment |
There was a problem hiding this comment.
1 issue found across 3 files
Confidence score: 3/5
- There is a concrete matching-risk in
lib/composio/matchSocialToConnector.ts: using string.includes()for URL matching can produce false positives and map socials to the wrong connector. - Given the medium severity (6/10) and high confidence (9/10), this is a meaningful behavior/regression concern rather than a housekeeping note, so merge risk is moderate.
- Pay close attention to
lib/composio/matchSocialToConnector.ts- hostname-based matching should be used to prevent incorrect connector/social associations.
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="lib/composio/matchSocialToConnector.ts">
<violation number="1" location="lib/composio/matchSocialToConnector.ts:17">
P2: Match against the URL hostname instead of string `.includes()` to avoid false-positive connector/social matches.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.
| ): Social | undefined { | ||
| const domain = CONNECTOR_SOCIAL_DOMAINS[slug]; | ||
| if (!domain) return undefined; | ||
| return socials.find((s) => s.profile_url?.toLowerCase().includes(domain)); |
There was a problem hiding this comment.
P2: Match against the URL hostname instead of string .includes() to avoid false-positive connector/social matches.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lib/composio/matchSocialToConnector.ts, line 17:
<comment>Match against the URL hostname instead of string `.includes()` to avoid false-positive connector/social matches.</comment>
<file context>
@@ -0,0 +1,18 @@
+): Social | undefined {
+ const domain = CONNECTOR_SOCIAL_DOMAINS[slug];
+ if (!domain) return undefined;
+ return socials.find((s) => s.profile_url?.toLowerCase().includes(domain));
+}
</file context>
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
components/ConnectorsPage/ConnectorCard.tsx (1)
45-52: Usenext/imagefor remote social avatars instead of raw<img>.The component already renders avatars from allowlisted hosts (Twitter, Discord, Facebook, Instagram, GitHub, Twitch, YouTube are all configured in
next.config.mjs), so switching tonext/imagehere is straightforward and unlocks Next.js optimization benefits: automatic resizing, modern format negotiation, and lazy loading. This aligns with Next.js performance best practices.import Image from "next/image"; // In the JSX: {showSocialProfile && socialAvatar ? ( <div className="relative"> <Image src={socialAvatar} alt={socialUsername || "Profile"} width={40} height={40} className="rounded-xl object-cover" /> <div className="absolute -bottom-1 -right-1 p-0.5 rounded-md bg-card"> {getConnectorIcon(connector.slug, 14)} </div> </div> ) : ( // ... )}Remove the ESLint suppression once you migrate—no lint suppression needed.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/ConnectorsPage/ConnectorCard.tsx` around lines 45 - 52, The component uses a raw <img> for socialAvatar which disables the Next.js image lint rule; replace it with next/image to enable optimizations: import Image from "next/image" at the top of ConnectorCard.tsx, render <Image src={socialAvatar} alt={socialUsername || "Profile"} width={40} height={40} className="rounded-xl object-cover" /> inside the existing conditional that checks showSocialProfile and socialAvatar, keep the small connector badge (getConnectorIcon(connector.slug, 14)) positioned as before, and remove the eslint-disable-next-line `@next/next/no-img-element` comment.
🤖 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/ArtistConnectorsTab.tsx`:
- Around line 23-29: The callback URL is being built from window.location inside
the useMemo in ArtistConnectorsTab (config -> callbackUrl), which runs during
render and will break server-side rendering; instead, stop reading window in the
render path by computing callbackUrl inside a useEffect (or derive it from
Next.js router state) and store it in state (e.g., callbackUrlState), then
include that state in the useMemo dependencies so config is created from
artistAccountId, ALLOWED_ARTIST_CONNECTORS and the callbackUrlState; update
references to use the new state-backed callback URL.
In `@lib/composio/matchSocialToConnector.ts`:
- Around line 15-17: The current matching uses
profile_url?.toLowerCase().includes(domain) which can false-match substrings;
instead parse profile_url with the URL constructor inside a try/catch and
compare the URL.hostname to the expected domain from
CONNECTOR_SOCIAL_DOMAINS[slug]: accept exact equality or hostname.endsWith('.' +
domain) to allow subdomains (e.g., www.instagram.com), and return undefined on
parse errors or non-matches; update the socials.find logic that references
profile_url to perform this hostname-based check using slug and
CONNECTOR_SOCIAL_DOMAINS.
---
Nitpick comments:
In `@components/ConnectorsPage/ConnectorCard.tsx`:
- Around line 45-52: The component uses a raw <img> for socialAvatar which
disables the Next.js image lint rule; replace it with next/image to enable
optimizations: import Image from "next/image" at the top of ConnectorCard.tsx,
render <Image src={socialAvatar} alt={socialUsername || "Profile"} width={40}
height={40} className="rounded-xl object-cover" /> inside the existing
conditional that checks showSocialProfile and socialAvatar, keep the small
connector badge (getConnectorIcon(connector.slug, 14)) positioned as before, and
remove the eslint-disable-next-line `@next/next/no-img-element` comment.
🪄 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: 5f7b2b68-e1bb-4462-b761-e0e4a5083a0f
📒 Files selected for processing (3)
components/ArtistSetting/ArtistConnectorsTab.tsxcomponents/ConnectorsPage/ConnectorCard.tsxlib/composio/matchSocialToConnector.ts
| const config = useMemo( | ||
| () => ({ | ||
| accountId: artistAccountId, | ||
| allowedSlugs: [...ALLOWED_ARTIST_CONNECTORS], | ||
| callbackUrl: `${window.location.origin}${window.location.pathname}?artist_connected=true&artist_id=${artistAccountId}`, | ||
| }), | ||
| [artistAccountId], |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
In Next.js App Router, can a "use client"component safely readwindowduring render, or must browser APIs be accessed after mount (for example inuseEffect or event handlers)?
💡 Result:
In the App Router, a "use client" component still participates in the initial server render to produce HTML (“Client Components and the RSC Payload are used to prerender HTML”) and is only made interactive after hydration. So it is not safe to read window (or other browser-only APIs) during render, because that render can run in a non-browser environment. Use browser APIs after mount (e.g., in useEffect/useLayoutEffect) or in event handlers (which only run in the browser). [1][2]
If you truly need code to run only in the browser even for the initial render, you must opt out of SSR for that component (e.g., next/dynamic with ssr: false). [1]
Sources
[1] Next.js Docs — Server and Client Components (Last updated Mar 31, 2026) (nextjs.org)
[2] React Docs — useEffect (“Effects only run on the client. They don’t run during server rendering.”) (react.dev)
Citations:
- 1: https://nextjs.org/docs/app/building-your-application/rendering/client-components
- 2: https://react.dev/reference/react/useEffect
Move callback URL construction out of the render path
useMemo runs during the render phase, and Next.js client components are still prerendered on the server before hydration. Reading window.location here will throw in the server environment and block rendering. Build the callback URL in useEffect or derive it from Next.js router state instead.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/ArtistSetting/ArtistConnectorsTab.tsx` around lines 23 - 29, The
callback URL is being built from window.location inside the useMemo in
ArtistConnectorsTab (config -> callbackUrl), which runs during render and will
break server-side rendering; instead, stop reading window in the render path by
computing callbackUrl inside a useEffect (or derive it from Next.js router
state) and store it in state (e.g., callbackUrlState), then include that state
in the useMemo dependencies so config is created from artistAccountId,
ALLOWED_ARTIST_CONNECTORS and the callbackUrlState; update references to use the
new state-backed callback URL.
| const domain = CONNECTOR_SOCIAL_DOMAINS[slug]; | ||
| if (!domain) return undefined; | ||
| return socials.find((s) => s.profile_url?.toLowerCase().includes(domain)); |
There was a problem hiding this comment.
Match on the hostname, not a substring.
includes(domain) also matches unrelated URLs like https://notinstagram.com/... or any URL that only mentions the platform in its path/query, so this can bind the wrong social record to a connector.
🔧 Safer match
export function matchSocialToConnector(
slug: string,
socials: Social[],
): Social | undefined {
const domain = CONNECTOR_SOCIAL_DOMAINS[slug];
if (!domain) return undefined;
- return socials.find((s) => s.profile_url?.toLowerCase().includes(domain));
+ return socials.find((social) => {
+ try {
+ const hostname = new URL(social.profile_url).hostname.toLowerCase();
+ return hostname === domain || hostname.endsWith(`.${domain}`);
+ } catch {
+ return false;
+ }
+ });
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/composio/matchSocialToConnector.ts` around lines 15 - 17, The current
matching uses profile_url?.toLowerCase().includes(domain) which can false-match
substrings; instead parse profile_url with the URL constructor inside a
try/catch and compare the URL.hostname to the expected domain from
CONNECTOR_SOCIAL_DOMAINS[slug]: accept exact equality or hostname.endsWith('.' +
domain) to allow subdomains (e.g., www.instagram.com), and return undefined on
parse errors or non-matches; update the socials.find logic that references
profile_url to perform this hostname-based check using slug and
CONNECTOR_SOCIAL_DOMAINS.
| const { data: socialsData } = useQuery({ | ||
| queryKey: ["artistSocials", artistAccountId], | ||
| queryFn: () => getArtistSocials(artistAccountId), | ||
| enabled: !!artistAccountId, | ||
| staleTime: 1000 * 60 * 5, | ||
| }); |
There was a problem hiding this comment.
This solution won't work currently. The getArtistSocials does not include data from artist connections. We should find a way to update the API to return the data you're expecting.
- actual: API for getArtistSocials does not include
connectionsin the response. - required: update the docs and API so getArtistSocials also returns the equivalent data from composio to augment the existing supabase data.
| <div className="shrink-0 relative"> | ||
| {showSocialProfile && socialAvatar ? ( | ||
| <div className="relative"> | ||
| {/* eslint-disable-next-line @next/next/no-img-element */} | ||
| <img | ||
| src={socialAvatar} | ||
| alt={socialUsername || "Profile"} | ||
| width={40} | ||
| height={40} | ||
| className="rounded-xl object-cover" | ||
| /> | ||
| <div className="absolute -bottom-1 -right-1 p-0.5 rounded-md bg-card"> | ||
| {getConnectorIcon(connector.slug, 14)} | ||
| </div> | ||
| </div> | ||
| ) : ( | ||
| <div className="p-2.5 rounded-xl transition-colors bg-muted/50 group-hover:bg-muted"> | ||
| {getConnectorIcon(connector.slug, 22)} | ||
| </div> | ||
| )} |
There was a problem hiding this comment.
Open closed principle
- actual: pfp component code added inline to ConnectorCard.
- required: new component file for the ConnectorPfp.
| const CONNECTOR_SOCIAL_DOMAINS: Record<string, string> = { | ||
| instagram: "instagram.com", | ||
| tiktok: "tiktok.com", | ||
| }; |
There was a problem hiding this comment.
KISS - why is this mapping needed? doesn't the Social type already include a platform tag?
Summary
Changes
ConnectorCard— accepts optionalsocialAvatarandsocialUsernameprops; renders PFP with connector icon badge overlay when availableArtistConnectorsTab— fetches artist socials via react-query and passes matched profile data to each connector cardmatchSocialToConnector— new utility to match a connector slug to its corresponding social profile by domainTest plan
🤖 Generated with Claude Code
Summary by cubic
Show the artist’s social profile picture and @username on connected Instagram and TikTok connectors in Artist Settings. Falls back to the default icon and description when no social data is available.
@tanstack/react-query(5 min cache) and match to connectors by domain usingmatchSocialToConnector.ConnectorCardnow acceptssocialAvatarandsocialUsernameto render a PFP with a connector badge and show the @username in the subtitle.Written for commit 0bbd0ec. Summary will update on new commits.
Summary by CodeRabbit
New Features