CompactMessageButton
A space-efficient messaging component that opens a popover for composing secure Bitcoin-based messages
A compact messaging component that provides a clean, space-efficient interface for composing and sending messages via Bitcoin transactions. Perfect for inline messaging in social feeds, user profiles, or chat interfaces where space is at a premium.
Installation
npx bigblocks add compact-message-button
Import
import { CompactMessageButton } from 'bigblocks';
Props
Prop | Type | Required | Default | Description |
---|---|---|---|---|
onMessage | (message: Message) => void | Promise<void> | Yes | - | Callback when message is sent |
recipientName | string | No | - | Display name of the message recipient |
recipientAvatar | string | No | - | Avatar URL of the recipient |
recipientAddress | string | Yes | - | Bitcoin address of the recipient |
defaultContent | string | No | '' | Pre-filled message content |
placeholder | string | No | 'Type a message...' | Input placeholder text |
maxLength | number | No | 500 | Maximum message length |
align | 'start' | 'center' | 'end' | No | 'end' | Popover alignment |
side | 'top' | 'right' | 'bottom' | 'left' | No | 'bottom' | Popover side |
iconSize | number | No | 16 | Size of the chat icon in pixels |
allowRecipientChange | boolean | No | false | Allow changing recipient in the popover |
variant | ButtonVariant | No | 'ghost' | Button style variant |
size | '1' | '2' | '3' | '4' | No | '2' | Button size |
disabled | boolean | No | false | Disable the button |
className | string | No | - | Additional CSS classes |
Basic Usage
import { CompactMessageButton } from 'bigblocks';
function UserProfile({ user }) {
return (
<CompactMessageButton
recipientName={user.name}
recipientAvatar={user.avatar}
recipientAddress={user.address}
onMessage={(message) => {
console.log('Message sent:', message);
}}
/>
);
}
Advanced Usage
import { CompactMessageButton } from 'bigblocks';
import { toast } from 'sonner';
function MessagingInterface() {
const handleMessage = async (message: Message) => {
try {
// Message is already broadcast to Bitcoin network
// You can store additional metadata in your backend
await saveMessageToDatabase({
txId: message.txId,
recipient: message.recipient,
timestamp: message.timestamp,
});
toast.success(`Message sent! TxID: ${message.txId.slice(0, 8)}...`);
} catch (error) {
toast.error('Failed to save message metadata');
}
};
return (
<CompactMessageButton
recipientName="Alice"
recipientAvatar="/avatars/alice.jpg"
recipientAddress="1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"
defaultContent="Hey Alice, "
placeholder="Write your message..."
maxLength={280}
align="start"
side="top"
iconSize={20}
variant="soft"
size="3"
onMessage={handleMessage}
/>
);
}
Message Object
The onMessage
callback receives a message object with the following structure:
interface Message {
txId: string; // Bitcoin transaction ID of the message
content: string; // The actual message text
recipient: string; // Recipient's Bitcoin address
timestamp: number; // Unix timestamp when message was sent
contentType: string; // MIME type (default: 'text/plain')
}
Common Patterns
In User Lists
Display compact message buttons next to user names in lists:
import { CompactMessageButton } from 'bigblocks';
function UserList({ users }) {
return (
<div className="space-y-2">
{users.map(user => (
<div key={user.id} className="flex items-center justify-between p-3 hover:bg-gray-50">
<div className="flex items-center gap-3">
<img src={user.avatar} className="w-10 h-10 rounded-full" />
<span className="font-medium">{user.name}</span>
</div>
<CompactMessageButton
recipientName={user.name}
recipientAvatar={user.avatar}
recipientAddress={user.address}
size="1"
variant="ghost"
/>
</div>
))}
</div>
);
}
In Social Feed Actions
Add messaging to post action bars:
import { CompactMessageButton, LikeButton, FollowButton } from 'bigblocks';
function PostActions({ post }) {
return (
<div className="flex items-center gap-2 mt-4">
<LikeButton postId={post.id} />
<CompactMessageButton
recipientName={post.author.name}
recipientAvatar={post.author.avatar}
recipientAddress={post.author.address}
size="2"
/>
<FollowButton userAddress={post.author.address} />
</div>
);
}
Direct Message Composer
Allow users to compose messages without a predefined recipient:
import { CompactMessageButton } from 'bigblocks';
import { useContacts } from '@/hooks/useContacts';
function DirectMessageComposer() {
const { contacts } = useContacts();
return (
<CompactMessageButton
allowRecipientChange={true}
placeholder="Start a conversation..."
variant="surface"
size="3"
onMessage={async (message) => {
// Handle the message
await sendDirectMessage(message);
toast.success('Message sent!');
}}
/>
);
}
Reply to Message
Pre-fill content for replies:
function MessageReply({ originalMessage }) {
return (
<CompactMessageButton
recipientName={originalMessage.senderName}
recipientAddress={originalMessage.senderAddress}
defaultContent={`Re: ${originalMessage.subject}`}
onMessage={handleReply}
size="1"
variant="ghost"
/>
);
}
Authentication Requirements
The CompactMessageButton requires Bitcoin authentication to send messages. Wrap your app with the necessary providers:
import {
BitcoinAuthProvider,
BitcoinQueryProvider,
BitcoinThemeProvider
} from 'bigblocks';
function App() {
return (
<BitcoinQueryProvider>
<BitcoinAuthProvider config={{ apiUrl: '/api' }}>
<BitcoinThemeProvider>
<YourApp />
</BitcoinThemeProvider>
</BitcoinAuthProvider>
</BitcoinQueryProvider>
);
}
When not authenticated, the button will be disabled automatically.
Popover Interface
The component opens a popover with the following layout:
┌─────────────────────────┐
│ Send Message │
│ To: 👤 John Doe │
├─────────────────────────┤
│ │
│ [Message text area] │
│ │
│ │
├─────────────────────────┤
│ 245 chars Cmd+Enter │
│ [✉️ Send] │
└─────────────────────────┘
Character Counter
The character counter provides visual feedback:
- Green: More than 50 characters remaining
- Orange: Less than 50 characters remaining
- Red: Over the character limit (send button disabled)
Keyboard Shortcuts
- Cmd/Ctrl + Enter: Send the message
- Escape: Close the popover
Bitcoin Integration
On-Chain Messaging
Messages are broadcast as Bitcoin transactions using the MAP (Magic Attribute Protocol) standard:
// Example MAP transaction structure
{
"app": "messaging",
"type": "message",
"content": "Hello Alice!",
"recipient": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
"timestamp": 1704067200000,
"contentType": "text/plain"
}
Cost Considerations
- Each message costs approximately $0.001 in transaction fees
- Messages are permanently stored on the blockchain
- Consider implementing off-chain storage for longer conversations
Privacy Notes
- Messages are public on the blockchain
- Consider encryption for sensitive communications
- Use the component's integration with BAP identity for verified senders
Styling
The component uses BigBlocks layout constants for consistent sizing:
// Internal styling uses:
CONTAINER_WIDTHS.POPOVER_SMALL // 320px popover width
Theme Adaptation
The component automatically adapts to your BitcoinThemeProvider settings:
- Button variants respect theme colors
- Text and borders follow theme contrast ratios
- Dark mode is fully supported
Custom Styling
Apply custom styles with the className prop:
<CompactMessageButton
className="hover:scale-105 transition-transform"
recipientAddress={address}
onMessage={handleMessage}
/>
Error Handling
function MessageWithErrorHandling() {
const handleMessage = async (message: Message) => {
try {
// Message is already broadcast, handle additional logic
await saveToDatabase(message);
} catch (error) {
if (error.code === 'INSUFFICIENT_FUNDS') {
toast.error('Not enough BSV to send message');
} else if (error.code === 'NETWORK_ERROR') {
toast.error('Network error. Please try again.');
} else {
toast.error('Failed to send message');
}
}
};
return (
<CompactMessageButton
recipientAddress="..."
onMessage={handleMessage}
/>
);
}
Best Practices
- Always Provide Recipient Info: Include name and avatar when available for better UX
- Handle Async Operations: The onMessage callback can be async for additional processing
- Size Appropriately: Match button size to surrounding UI elements
- Provide Feedback: Show success/error states after message operations
- Consider Rate Limiting: Implement client-side rate limiting for high-traffic applications
- Test Authentication States: Ensure graceful handling when users aren't authenticated
Accessibility
- ARIA Labels: Button includes proper labels for screen readers
- Keyboard Navigation: Full keyboard support including focus management
- Disabled States: Clear visual indicators when button is disabled
- Loading States: Accessible loading indicators during message sending
Troubleshooting
Button Disabled
- Solution: Ensure user is authenticated with BitcoinAuthProvider
- Check: Verify Bitcoin keypair is available in session
Message Not Sending
- Solution: Check Bitcoin balance for transaction fees
- Verify: Network connectivity and API endpoint configuration
Popover Not Opening
- Solution: Check z-index conflicts with other UI elements
- Verify: No parent elements have
overflow: hidden
Related Components
- PostButton - For creating posts
- MessageDisplay - For displaying messages
- SocialFeed - For message feeds
- ProfilePopover - For user profiles
API Reference
Message Type
interface Message {
txId: string; // Bitcoin transaction ID
content: string; // Message content
recipient: string; // Recipient Bitcoin address
timestamp: number; // Unix timestamp
contentType: string; // MIME type
}
ButtonVariant Type
type ButtonVariant =
| 'solid'
| 'soft'
| 'surface'
| 'outline'
| 'ghost'
| 'classic';