useSocial
Comprehensive hook combining all social operations (posts, likes, follows, friends) into a single interface
useSocial
A powerful hook that combines all social operations into a single, unified interface. It provides methods for creating posts, managing likes, following users, and handling friend relationships on the Bitcoin blockchain.
Installation
npx bigblocks add use-social
Import
import { useSocial } from 'bigblocks';
Return Value
The hook returns an object with the following properties:
Post Operations
Method | Type | Description |
---|---|---|
createPost | (data: PostMutationData) => void | Create a new post |
createPostAsync | (data: PostMutationData) => Promise<BroadcastResult> | Async version returning promise |
Follow Operations
Method | Type | Description |
---|---|---|
followUser | (data: FollowMutationData) => void | Follow a user |
followUserAsync | (data: FollowMutationData) => Promise<BroadcastResult> | Async version |
unfollowUser | (data: FollowMutationData) => void | Unfollow a user |
unfollowUserAsync | (data: FollowMutationData) => Promise<BroadcastResult> | Async version |
Like Operations
Method | Type | Description |
---|---|---|
likePost | (data: LikeMutationData) => void | Like a post |
likePostAsync | (data: LikeMutationData) => Promise<BroadcastResult> | Async version |
unlikePost | (data: LikeMutationData) => void | Unlike a post |
unlikePostAsync | (data: LikeMutationData) => Promise<BroadcastResult> | Async version |
Friend Operations
Method | Type | Description |
---|---|---|
acceptFriendRequest | (idKey: string) => void | Accept a friend request |
declineFriendRequest | (idKey: string) => void | Decline a friend request |
State Management
Property | Type | Description |
---|---|---|
isLoading | boolean | Any operation is loading |
isError | boolean | Any operation has error |
isSuccess | boolean | Last operation succeeded |
hasError | boolean | Boolean error check |
error | SocialError | null | Error object if any |
reset | () => void | Reset all operation states |
Data
Property | Type | Description |
---|---|---|
friends | Friend[] | Array of friend objects |
friendRequests | BapIdentity[] | Array of pending friend requests |
Type Definitions
PostMutationData
interface PostMutationData {
content: string;
contentType?: 'text/plain' | 'text/markdown';
app?: string;
}
FollowMutationData
interface FollowMutationData {
idKey: string; // BAP identity key
}
LikeMutationData
interface LikeMutationData {
txid: string;
emoji?: string; // Default: '👍'
}
BroadcastResult
interface BroadcastResult {
success: boolean;
txid?: string;
rawTx?: string;
error?: string;
fee?: number;
}
SocialError
interface SocialError {
code: SocialErrorCode;
message: string;
details?: Error | Record<string, unknown>;
}
type SocialErrorCode =
| 'TRANSACTION_FAILED'
| 'INVALID_CONTENT'
| 'USER_NOT_FOUND'
| 'INSUFFICIENT_FUNDS'
| 'NETWORK_ERROR'
| 'BROADCAST_FAILED'
| 'ENCRYPTION_FAILED'
| 'INVALID_BAP_ID'
| 'RATE_LIMITED'
| 'CONTENT_TOO_LONG'
| 'UNAUTHORIZED'
| 'UNKNOWN_ERROR';
Basic Usage
import { useSocial } from 'bigblocks';
function SocialDashboard() {
const {
createPost,
likePost,
followUser,
friends,
isLoading,
error
} = useSocial();
const handlePost = () => {
createPost({
content: "Hello Bitcoin world!",
contentType: 'text/plain'
});
};
return (
<div>
<button
onClick={handlePost}
disabled={isLoading}
>
Create Post
</button>
{error && <div>Error: {error.message}</div>}
<div>Friends: {friends.length}</div>
</div>
);
}
Advanced Usage
Complete Social Implementation
import { useSocial } from 'bigblocks';
function SocialHub() {
const {
createPostAsync,
likePost,
unlikePost,
followUserAsync,
unfollowUserAsync,
acceptFriendRequest,
declineFriendRequest,
friends,
friendRequests,
isLoading,
error,
reset
} = useSocial();
const [postContent, setPostContent] = useState('');
const [followingStatus, setFollowingStatus] = useState<Map<string, boolean>>(new Map());
// Handle post creation with async/await
const handleCreatePost = async () => {
try {
const result = await createPostAsync({
content: postContent,
contentType: 'text/markdown',
app: 'my-social-app'
});
if (result.success) {
console.log('Post created:', result.txid);
setPostContent('');
// Show success notification
}
} catch (err) {
console.error('Failed to create post:', err);
}
};
// Toggle follow/unfollow
const toggleFollow = async (idKey: string) => {
const isFollowing = followingStatus.get(idKey);
try {
if (isFollowing) {
await unfollowUserAsync({ idKey });
setFollowingStatus(prev => new Map(prev).set(idKey, false));
} else {
await followUserAsync({ idKey });
setFollowingStatus(prev => new Map(prev).set(idKey, true));
}
} catch (err) {
console.error('Follow operation failed:', err);
}
};
// Handle friend requests
const handleFriendRequest = (requestId: string, accept: boolean) => {
if (accept) {
acceptFriendRequest(requestId);
} else {
declineFriendRequest(requestId);
}
};
// Auto-clear errors after 5 seconds
useEffect(() => {
if (error) {
const timer = setTimeout(reset, 5000);
return () => clearTimeout(timer);
}
}, [error, reset]);
return (
<div className="space-y-4">
{/* Post Creation */}
<div>
<textarea
value={postContent}
onChange={(e) => setPostContent(e.target.value)}
placeholder="What's on your mind?"
maxLength={217}
disabled={isLoading}
/>
<button
onClick={handleCreatePost}
disabled={isLoading || !postContent.trim()}
>
{isLoading ? 'Posting...' : 'Post'}
</button>
</div>
{/* Friend Requests */}
{friendRequests.length > 0 && (
<div>
<h3>Friend Requests ({friendRequests.length})</h3>
{friendRequests.map(request => (
<div key={request.idKey}>
<span>{request.alternateName || request.idKey}</span>
<button
onClick={() => handleFriendRequest(request.idKey, true)}
disabled={isLoading}
>
Accept
</button>
<button
onClick={() => handleFriendRequest(request.idKey, false)}
disabled={isLoading}
>
Decline
</button>
</div>
))}
</div>
)}
{/* Friends List */}
<div>
<h3>Friends ({friends.length})</h3>
{friends.map(friend => (
<div key={friend.idKey}>
{friend.alternateName || friend.idKey}
</div>
))}
</div>
{/* Error Display */}
{error && (
<div className="error-banner">
{error.code}: {error.message}
<button onClick={reset}>Dismiss</button>
</div>
)}
</div>
);
}
Common Patterns
Social Feed with Actions
function SocialFeed({ posts }) {
const { likePost, unlikePost, followUser } = useSocial();
const [likedPosts, setLikedPosts] = useState<Set<string>>(new Set());
const handleLike = (txid: string) => {
if (likedPosts.has(txid)) {
unlikePost({ txid });
setLikedPosts(prev => {
const next = new Set(prev);
next.delete(txid);
return next;
});
} else {
likePost({ txid, emoji: '❤️' });
setLikedPosts(prev => new Set(prev).add(txid));
}
};
return (
<div>
{posts.map(post => (
<article key={post.txid}>
<header>
<span>{post.author.name}</span>
<button
onClick={() => followUser({ idKey: post.author.idKey })}
>
Follow
</button>
</header>
<p>{post.content}</p>
<footer>
<button
onClick={() => handleLike(post.txid)}
className={likedPosts.has(post.txid) ? 'liked' : ''}
>
{likedPosts.has(post.txid) ? '❤️' : '🤍'} Like
</button>
</footer>
</article>
))}
</div>
);
}
Error Handling with Retry
function RobustSocialActions() {
const { createPostAsync, error, reset } = useSocial();
const [retryCount, setRetryCount] = useState(0);
const maxRetries = 3;
const createPostWithRetry = async (content: string) => {
try {
await createPostAsync({ content });
setRetryCount(0); // Reset on success
} catch (err) {
if (retryCount < maxRetries) {
setRetryCount(prev => prev + 1);
// Exponential backoff
setTimeout(() => {
createPostWithRetry(content);
}, Math.pow(2, retryCount) * 1000);
}
}
};
return (
<div>
{error && (
<div>
Error: {error.message}
{retryCount > 0 && ` (Retry ${retryCount}/${maxRetries})`}
</div>
)}
{/* Your UI here */}
</div>
);
}
Optimistic UI Updates
function OptimisticSocialFeed() {
const { likePostAsync } = useSocial();
const [optimisticLikes, setOptimisticLikes] = useState<Map<string, boolean>>(new Map());
const handleLike = async (txid: string) => {
// Optimistically update UI
setOptimisticLikes(prev => new Map(prev).set(txid, true));
try {
await likePostAsync({ txid });
// Success - keep the optimistic update
} catch (err) {
// Revert on failure
setOptimisticLikes(prev => {
const next = new Map(prev);
next.delete(txid);
return next;
});
}
};
return (
<div>
{/* Show likes including optimistic updates */}
</div>
);
}
Authentication Requirements
The useSocial
hook requires proper authentication context:
import {
BitcoinAuthProvider,
BitcoinQueryProvider,
BitcoinThemeProvider
} from 'bigblocks';
function App() {
return (
<BitcoinQueryProvider>
<BitcoinAuthProvider config={{ apiUrl: '/api' }}>
<BitcoinThemeProvider>
<YourComponent />
</BitcoinThemeProvider>
</BitcoinAuthProvider>
</BitcoinQueryProvider>
);
}
API Integration
Required Endpoints
The hook expects the following API endpoints:
POST /api/social/post
- Create postsPOST /api/social/like
- Like postsPOST /api/social/unlike
- Unlike postsPOST /api/social/follow
- Follow usersPOST /api/social/unfollow
- Unfollow usersPOST /api/social/friend/accept
- Accept friend requestsPOST /api/social/friend/decline
- Decline friend requestsGET /api/social/friends
- Get friends list
Bitcoin Transaction Flow
- User initiates social action
- Hook creates and signs Bitcoin transaction
- Transaction broadcasted to BSV network
- API returns transaction ID
- UI updates with result
Troubleshooting
INSUFFICIENT_FUNDS Error
The user's wallet doesn't have enough BSV for the transaction fee:
if (error?.code === 'INSUFFICIENT_FUNDS') {
return <div>Please add funds to perform social actions</div>;
}
RATE_LIMITED Error
Too many requests in a short time:
if (error?.code === 'RATE_LIMITED') {
return <div>Please wait before performing more actions</div>;
}
CONTENT_TOO_LONG Error
Posts are limited to 217 characters on-chain:
const MAX_POST_LENGTH = 217;
if (postContent.length > MAX_POST_LENGTH) {
return <div>Post is too long ({postContent.length}/{MAX_POST_LENGTH})</div>;
}
Network Errors
Handle network connectivity issues:
if (error?.code === 'NETWORK_ERROR') {
return (
<div>
Network error. Check your connection.
<button onClick={reset}>Retry</button>
</div>
);
}
Best Practices
- Error Boundaries: Wrap social components in error boundaries
- Loading States: Always show loading indicators during operations
- Optimistic Updates: Update UI immediately for better UX
- Batch Operations: Use async methods for multiple operations
- Content Validation: Validate content length and format before posting
- Rate Limiting: Implement client-side rate limiting
- State Persistence: Consider persisting social state across sessions
Related Components
- PostButton - Simple post creation button
- LikeButton - Like/unlike button
- FollowButton - Follow/unfollow button
- SocialFeed - Complete social feed
- FriendsDialog - Friend management UI
API Reference
Hook Configuration
While useSocial
doesn't accept direct configuration, it uses the BitcoinAuthProvider
config:
<BitcoinAuthProvider config={{
apiUrl: '/api',
network: 'mainnet', // or 'testnet'
broadcaster: 'https://api.whatsonchain.com/v1/bsv/main/tx/raw'
}}>
Social Protocol Standards
The hook implements standard Bitcoin social protocols:
- B Protocol: For content storage
- MAP Protocol: For metadata
- BAP Protocol: For identity
- AIP Protocol: For authentication signatures