diff --git a/app/protected/connections/page.tsx b/app/protected/connections/page.tsx index 540d82b0..b23cc66d 100644 --- a/app/protected/connections/page.tsx +++ b/app/protected/connections/page.tsx @@ -42,10 +42,10 @@ export default function ConnectionsPage() { {/* Main Content */} -
+
- - + + Following @@ -60,16 +60,20 @@ export default function ConnectionsPage() { - - + +
+ +
- - + +
+ +
- -
+ +
)}
- +
+ +
diff --git a/components/connections/MutualConnections.tsx b/components/connections/MutualConnections.tsx new file mode 100644 index 00000000..f14c5217 --- /dev/null +++ b/components/connections/MutualConnections.tsx @@ -0,0 +1,103 @@ +'use client' + +import React, { useEffect, useState } from 'react' +import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar' +import { Users } from 'lucide-react' +import { connectionService } from '@/lib/services/connectionService' + +interface MutualConnectionsProps { + userId: string + className?: string +} + +interface MutualUser { + id: string + first_name: string | null + last_name: string | null + username: string + avatar_url: string | null +} + +export function MutualConnections({ userId, className = '' }: MutualConnectionsProps) { + const [mutualData, setMutualData] = useState<{ + count: number + users: MutualUser[] + }>({ count: 0, users: [] }) + const [loading, setLoading] = useState(true) + + useEffect(() => { + const loadMutualConnections = async () => { + try { + const data = await connectionService.getMutualConnections(userId) + setMutualData(data) + } catch (error) { + console.error('Error loading mutual connections:', error) + } finally { + setLoading(false) + } + } + + loadMutualConnections() + }, [userId]) + + if (loading || mutualData.count === 0) { + return null + } + + const displayUsers = mutualData.users.slice(0, 3) + const remainingCount = mutualData.count - displayUsers.length + + const getName = (user: MutualUser) => { + if (user.first_name && user.last_name) { + return `${user.first_name} ${user.last_name}` + } + return user.username + } + + const getInitials = (user: MutualUser) => { + if (user.first_name && user.last_name) { + return `${user.first_name[0]}${user.last_name[0]}`.toUpperCase() + } + return user.username.slice(0, 2).toUpperCase() + } + + return ( +
+ +
+ {/* Avatar stack */} +
+ {displayUsers.map((user) => ( + + {user.avatar_url && } + + {getInitials(user)} + + + ))} +
+ + {/* Text */} + + Connected with{' '} + + {displayUsers[0] && getName(displayUsers[0])} + + {displayUsers.length > 1 && ( + <> + {' '}and{' '} + + {remainingCount > 0 + ? `${displayUsers.length - 1 + remainingCount} others` + : displayUsers.length === 2 + ? getName(displayUsers[1]) + : `${displayUsers.length - 1} others` + } + + + )} + +
+
+ ) +} diff --git a/components/connections/UserCard.tsx b/components/connections/UserCard.tsx index db624cd6..ff295170 100644 --- a/components/connections/UserCard.tsx +++ b/components/connections/UserCard.tsx @@ -9,6 +9,7 @@ import { connectionService } from '@/lib/services/connectionService' import { conversationService } from '@/lib/services/conversationService' import { useRouter } from 'next/navigation' import { UserProfileModal } from './UserProfileModal' +import { MutualConnections } from './MutualConnections' interface UserCardProps { user: { @@ -91,7 +92,7 @@ export function UserCard({ user, connectionStatus, onConnectionChange, showMessa return ( <> -
+
{/* Clickable Avatar */}
-
-
- {/* Clickable Name */} + {/* User Info Section */} +
+ {/* Clickable Name */} + +
-
- - {localStatus?.isMutual && ( - - - Connected - - )} - {!localStatus?.isMutual && localStatus?.isFollowing && ( - - - Following - - )} - {!localStatus?.isMutual && localStatus?.isFollower && ( - - - Follows you - - )} -
- {user.bio && ( -

{user.bio}

+ {localStatus?.isMutual && ( + + + Connected + + )} + {!localStatus?.isMutual && localStatus?.isFollowing && ( + + + Following + + )} + {!localStatus?.isMutual && localStatus?.isFollower && ( + + + Follows you + )}
+ {user.bio && ( +

{user.bio}

+ )} + {/* Mutual Connections */} + +
-
- {/* View Profile Button */} + {/* Action Buttons - Stack on mobile, inline on desktop */} +
+ {/* View Profile Button */} + + + {showMessageButton && localStatus?.isMutual && ( - - {showMessageButton && localStatus?.isMutual && ( - - )} - - {localStatus?.isFollowing ? ( - - ) : ( - - )} -
+ )} + + {localStatus?.isFollowing ? ( + + ) : ( + + )}
diff --git a/components/connections/UserProfileModal.tsx b/components/connections/UserProfileModal.tsx index 7319f374..05210230 100644 --- a/components/connections/UserProfileModal.tsx +++ b/components/connections/UserProfileModal.tsx @@ -29,6 +29,7 @@ import { connectionService } from '@/lib/services/connectionService' import { conversationService } from '@/lib/services/conversationService' import { useRouter } from 'next/navigation' import Link from 'next/link' +import { MutualConnections } from './MutualConnections' interface UserProfileModalProps { userId: string @@ -214,6 +215,9 @@ export function UserProfileModal({ )}
+ {/* Mutual Connections */} + + {/* Action Buttons */}
{connectionStatus.isMutual && ( diff --git a/lib/services/connectionService.ts b/lib/services/connectionService.ts index 75b0a262..5b152269 100644 --- a/lib/services/connectionService.ts +++ b/lib/services/connectionService.ts @@ -179,6 +179,61 @@ export class ConnectionService { return { isFollowing, isFollower, isMutual } } + + // Get mutual connections between current user and another user + async getMutualConnections(userId: string): Promise<{ + count: number + users: Array<{ + id: string + first_name: string | null + last_name: string | null + username: string + avatar_url: string | null + }> + }> { + const supabase = this.getSupabaseClient() + const { data: { user } } = await supabase.auth.getUser() + + if (!user) { + return { count: 0, users: [] } + } + + // Get users that both current user and target user follow + const { data: currentUserFollowing } = await supabase + .from('user_connections') + .select('following_id') + .eq('follower_id', user.id) + + const { data: targetUserFollowing } = await supabase + .from('user_connections') + .select('following_id') + .eq('follower_id', userId) + + if (!currentUserFollowing || !targetUserFollowing) { + return { count: 0, users: [] } + } + + // Find mutual following + const currentFollowingIds = new Set(currentUserFollowing.map(c => c.following_id)) + const mutualIds = targetUserFollowing + .map(c => c.following_id) + .filter(id => currentFollowingIds.has(id) && id !== user.id && id !== userId) + + if (mutualIds.length === 0) { + return { count: 0, users: [] } + } + + // Get profile info for first 3 mutual connections + const { data: profiles } = await supabase + .from('profiles') + .select('id, first_name, last_name, username, avatar_url') + .in('id', mutualIds.slice(0, 3)) + + return { + count: mutualIds.length, + users: profiles || [] + } + } } export const connectionService = new ConnectionService()