BigBlocks Docs
Components/Social

FriendsDialog

Comprehensive friends management dialog with social networking features for Bitcoin-based applications

A comprehensive friends management dialog component that provides social networking functionality including friend lists, friend requests, mutual connections, and social discovery for Bitcoin-based applications. This component enables users to build trusted social networks using their Bitcoin identity as the foundation.

Demo Note: This demonstrates the FriendsDialog component for managing social connections. The dialog includes friend lists, requests, mutual connections, and discovery features.

View Component Preview →

Installation

npx bigblocks add friends-dialog

Import

import { FriendsDialog } from 'bigblocks';

Props

PropTypeRequiredDefaultDescription
isOpenbooleanYes-Dialog open state
onClose() => voidYes-Close dialog callback
onFriendRequest(bapId: string) => voidNo-Send friend request callback
onAcceptRequest(requestId: string) => voidNo-Accept friend request callback
onRejectRequest(requestId: string) => voidNo-Reject friend request callback
onRemoveFriend(bapId: string) => voidNo-Remove existing friend callback
onBlockUser(bapId: string) => voidNo-Block user callback
onUnblockUser(bapId: string) => voidNo-Unblock user callback
onMessageFriend(bapId: string) => voidNo-Start conversation callback
showMutualFriendsbooleanNotrueShow mutual connections count
enableFriendSuggestionsbooleanNotrueShow friend suggestions tab
maxResultsnumberNo50Maximum results per section
classNamestringNo-Additional CSS classes

Basic Usage

import { FriendsDialog } from 'bigblocks';
import { useState } from 'react';

function SocialApp() {
  const [friendsOpen, setFriendsOpen] = useState(false);
  
  return (
    <div>
      <button onClick={() => setFriendsOpen(true)}>
        Friends
      </button>
      
      <FriendsDialog 
        isOpen={friendsOpen}
        onClose={() => setFriendsOpen(false)}
        onFriendRequest={(bapId) => console.log('Friend request sent to:', bapId)}
        onAcceptRequest={(requestId) => console.log('Accepted request:', requestId)}
      />
    </div>
  );
}

Advanced Usage

With All Features Enabled

<FriendsDialog 
  isOpen={isOpen}
  onClose={handleClose}
  onFriendRequest={async (bapId) => {
    const result = await sendFriendRequest(bapId);
    toast.success('Friend request sent!');
  }}
  onAcceptRequest={async (requestId) => {
    await acceptFriendRequest(requestId);
    toast.success('Friend request accepted!');
  }}
  onRejectRequest={async (requestId) => {
    await rejectFriendRequest(requestId);
  }}
  onRemoveFriend={async (bapId) => {
    if (confirm('Remove this friend?')) {
      await removeFriend(bapId);
    }
  }}
  onBlockUser={async (bapId) => {
    if (confirm('Block this user?')) {
      await blockUser(bapId);
      toast.info('User blocked');
    }
  }}
  onMessageFriend={(bapId) => {
    router.push(`/messages/${bapId}`);
  }}
  showMutualFriends={true}
  enableFriendSuggestions={true}
  maxResults={100}
/>

Common Patterns

Social Discovery Integration

function SocialDiscoveryApp() {
  const [activeTab, setActiveTab] = useState('friends');
  const { user } = useBitcoinAuth();
  const { friends, suggestions, nearbyUsers } = useSocialData();
  
  return (
    <FriendsDialog
      isOpen={true}
      onClose={() => {}}
      customTabs={[
        {
          id: 'discovery',
          label: 'Discover',
          content: (
            <div className="discovery-content">
              <div className="discovery-filters">
                <button 
                  className={activeTab === 'suggested' ? 'active' : ''}
                  onClick={() => setActiveTab('suggested')}
                >
                  Suggested for You
                </button>
                <button 
                  className={activeTab === 'nearby' ? 'active' : ''}
                  onClick={() => setActiveTab('nearby')}
                >
                  Nearby Users
                </button>
                <button 
                  className={activeTab === 'mutual' ? 'active' : ''}
                  onClick={() => setActiveTab('mutual')}
                >
                  Friends of Friends
                </button>
              </div>
              
              {activeTab === 'suggested' && (
                <SuggestedUsersList 
                  suggestions={suggestions}
                  onConnect={(bapId) => sendFriendRequest(bapId)}
                />
              )}
              
              {activeTab === 'nearby' && (
                <NearbyUsersList 
                  users={nearbyUsers}
                  onConnect={(bapId) => sendFriendRequest(bapId)}
                />
              )}
              
              {activeTab === 'mutual' && (
                <MutualFriendsList 
                  friends={friends}
                  onConnect={(bapId) => sendFriendRequest(bapId)}
                />
              )}
            </div>
          )
        }
      ]}
    />
  );
}

Gaming Friends System

function GamingFriendsManager() {
  const { onlineFriends, recentlyPlayed } = useGamingFriends();
  
  return (
    <FriendsDialog
      isOpen={true}
      onClose={() => {}}
      customTabs={[
        {
          id: 'online',
          label: `Online (${onlineFriends.length})`,
          content: (
            <div className="online-friends">
              {onlineFriends.map(friend => (
                <div key={friend.bapId} className="online-friend">
                  <div className="friend-status online" />
                  <BitcoinAvatar 
                    src={friend.avatar}
                    name={friend.name}
                    size="small"
                  />
                  <div className="friend-info">
                    <h5>{friend.name}</h5>
                    <p className="playing-game">
                      Playing: {friend.currentGame}
                    </p>
                  </div>
                  <div className="friend-actions">
                    <button onClick={() => inviteToGame(friend.bapId)}>
                      Invite
                    </button>
                    <button onClick={() => joinGame(friend.gameId)}>
                      Join
                    </button>
                  </div>
                </div>
              ))}
            </div>
          )
        }
      ]}
    />
  );
}

Professional Networking

function ProfessionalNetwork() {
  const { connections, pendingConnections } = useProfessionalNetwork();
  
  return (
    <FriendsDialog
      isOpen={true}
      onClose={() => {}}
      showMutualFriends={true}
      customTabs={[
        {
          id: 'connections',
          label: 'My Network',
          content: (
            <div className="professional-network">
              {connections.map(connection => (
                <div key={connection.bapId} className="connection-card">
                  <BitcoinAvatar 
                    src={connection.avatar}
                    name={connection.name}
                  />
                  <div className="connection-details">
                    <h4>{connection.name}</h4>
                    <p className="title">{connection.title}</p>
                    <p className="company">{connection.company}</p>
                    <div className="mutual-info">
                      {connection.mutualConnections} mutual connections
                    </div>
                  </div>
                  <button 
                    className="message-btn"
                    onClick={() => startConversation(connection.bapId)}
                  >
                    Message
                  </button>
                </div>
              ))}
            </div>
          )
        }
      ]}
    />
  );
}

Authentication Requirements

The FriendsDialog component requires proper Bitcoin authentication context:

import { 
  BitcoinAuthProvider, 
  BitcoinQueryProvider,
  BitcoinThemeProvider 
} from 'bigblocks';

function App() {
  return (
    <BitcoinQueryProvider>
      <BitcoinAuthProvider config={{ apiUrl: '/api' }}>
        <BitcoinThemeProvider>
          <YourAppWithFriendsDialog />
        </BitcoinThemeProvider>
      </BitcoinAuthProvider>
    </BitcoinQueryProvider>
  );
}

API Reference

This component extends the Dialog primitive and provides comprehensive social networking functionality for Bitcoin-based applications.

API Integration

The FriendsDialog component typically requires these backend endpoints:

Friend Management

  • GET /api/friends - Fetch user's friend list
  • POST /api/friends/request - Send friend request
  • POST /api/friends/accept - Accept friend request
  • POST /api/friends/reject - Reject friend request
  • DELETE /api/friends/:bapId - Remove friend
  • POST /api/friends/block - Block user
  • DELETE /api/friends/block/:bapId - Unblock user

Social Discovery

  • GET /api/friends/suggestions - Get friend suggestions
  • GET /api/friends/mutual/:bapId - Get mutual friends
  • GET /api/friends/nearby - Get nearby users (if location enabled)

Example API Implementation

// /api/friends/request
export async function POST(request: Request) {
  const { bapId } = await request.json();
  const session = await getSession();
  
  if (!session?.user?.bapId) {
    return new Response('Unauthorized', { status: 401 });
  }
  
  // Create friend request with Bitcoin signature
  const requestData = {
    from: session.user.bapId,
    to: bapId,
    timestamp: Date.now(),
    status: 'pending'
  };
  
  // Sign the request with user's Bitcoin key
  const signature = await signWithBitcoin(requestData);
  
  // Store in database
  await redis.hset(
    `friend-requests:${bapId}`,
    session.user.bapId,
    JSON.stringify({ ...requestData, signature })
  );
  
  return Response.json({ success: true, requestId: signature });
}

Troubleshooting

Friends List Not Loading

// Ensure BitcoinQueryProvider is wrapping the component
<BitcoinQueryProvider>
  <FriendsDialog {...props} />
</BitcoinQueryProvider>

Friend Requests Not Working

// Check that callbacks are properly async
onFriendRequest={async (bapId) => {
  try {
    await sendFriendRequest(bapId);
  } catch (error) {
    console.error('Friend request failed:', error);
  }
}}

Real-time Updates Not Working

// Implement WebSocket or polling for real-time updates
useEffect(() => {
  const interval = setInterval(() => {
    refetchFriends();
    refetchRequests();
  }, 30000); // Poll every 30 seconds
  
  return () => clearInterval(interval);
}, []);

Advanced Features

Custom Tab Implementation

interface CustomTab {
  id: string;
  label: string;
  content: React.ReactNode;
  badge?: number;
}

const customTabs: CustomTab[] = [
  {
    id: 'groups',
    label: 'Groups',
    badge: 3,
    content: <GroupsList />
  },
  {
    id: 'events',
    label: 'Events',
    content: <EventInvitations />
  }
];

<FriendsDialog
  customTabs={customTabs}
  defaultTab="groups"
/>

Friend Filtering and Sorting

const [sortBy, setSortBy] = useState<'name' | 'activity' | 'reputation'>('name');
const [filterBy, setFilterBy] = useState<'all' | 'online' | 'verified'>('all');

const filteredFriends = useMemo(() => {
  return friends
    .filter(friend => {
      if (filterBy === 'online') return friend.isOnline;
      if (filterBy === 'verified') return friend.verified;
      return true;
    })
    .sort((a, b) => {
      switch (sortBy) {
        case 'name':
          return a.name.localeCompare(b.name);
        case 'activity':
          return b.lastSeen - a.lastSeen;
        case 'reputation':
          return b.reputation - a.reputation;
        default:
          return 0;
      }
    });
}, [friends, sortBy, filterBy]);

Privacy Controls

const privacySettings = {
  showOnlineStatus: true,
  allowFriendRequests: true,
  showMutualFriends: true,
  requireApproval: true,
  blockList: ['bapId1', 'bapId2']
};

<FriendsDialog
  privacySettings={privacySettings}
  onUpdatePrivacy={(newSettings) => updatePrivacySettings(newSettings)}
/>

Security Considerations

  1. Bitcoin Signature Verification: All friend operations should be signed with the user's Bitcoin private key
  2. Privacy Settings: Respect user privacy preferences when displaying mutual friends or online status
  3. Rate Limiting: Implement rate limits on friend requests to prevent spam
  4. Block List Management: Ensure blocked users cannot view any information about the blocking user
  5. Data Encryption: Consider encrypting sensitive friend data in transit and at rest

Best Practices

  1. Pagination: For large friend lists, implement virtual scrolling or pagination
  2. Caching: Use React Query or similar for efficient data caching
  3. Optimistic Updates: Update UI immediately while API calls complete
  4. Error Handling: Provide clear feedback for failed operations
  5. Mobile Responsiveness: Ensure the dialog works well on mobile devices
  6. Accessibility: Include proper ARIA labels and keyboard navigation