Notifications
CredDAO uses a context-based toast notification system for user feedback.
Setup
Wrap your app with ToastProvider:
import { ToastProvider } from '@/components/notifications/Toast'
<ToastProvider>
<App />
</ToastProvider>Usage
Basic Usage
import { useToast } from '@/components/notifications/Toast'
function Component() {
const { success, error, warning, info } = useToast()
const handleAction = async () => {
try {
await doSomething()
success('Action completed!')
} catch (err) {
error('Action failed', err.message)
}
}
}Notification Types
// Success (green) - 5s duration
success('Vote cast successfully!')
// Error (red) - 8s duration
error('Transaction failed', 'Insufficient balance')
// Warning (amber) - 5s duration
warning('Voting ends in 1 hour')
// Info (blue) - 5s duration
info('New proposal available')Custom Duration
const { addToast } = useToast()
addToast({
type: 'info',
title: 'Processing...',
message: 'This may take a few seconds',
duration: 10000 // 10 seconds
})
// No auto-dismiss
addToast({
type: 'info',
title: 'Persistent notification',
duration: 0 // Manual dismiss only
})API Reference
useToast Hook
interface ToastContextValue {
toasts: Toast[]
addToast: (toast: Omit<Toast, 'id'>) => void
removeToast: (id: string) => void
success: (title: string, message?: string) => void
error: (title: string, message?: string) => void
warning: (title: string, message?: string) => void
info: (title: string, message?: string) => void
}
interface Toast {
id: string
type: 'success' | 'error' | 'warning' | 'info'
title: string
message?: string
duration?: number
}Integration Examples
Transaction Feedback
const { success, error, warning } = useToast()
const { execute, status } = useTransaction()
const handleVote = async () => {
warning('Confirming vote...', 'Please wait')
const sig = await execute(async () => {
const tx = await createVoteTransaction()
await wallet.sendTransaction(tx, connection)
return { signature: tx.signature }
})
if (sig) {
success('Vote confirmed!', `Signature: ${sig.slice(0, 8)}...`)
} else {
error('Vote failed', 'Transaction was rejected')
}
}Form Submission
const handleSubmit = async (data: FormData) => {
const toast = useToast()
try {
await createProposal(data)
toast.success('Proposal created!', 'Voting starts in 1 hour')
router.push('/proposals')
} catch (err) {
if (err.message.includes('InsufficientReputationTier')) {
toast.error('Insufficient tier', 'Gold tier required for this proposal type')
} else {
toast.error('Failed to create proposal', err.message)
}
}
}API Errors
// Global error handler
const handleApiError = (error: unknown, toast: ReturnType<typeof useToast>) => {
if (error instanceof Error) {
if (error.message.includes('429')) {
toast.error('Rate limited', 'Please wait before trying again')
} else if (error.message.includes('401')) {
toast.error('Unauthorized', 'Please reconnect your wallet')
} else {
toast.error('Request failed', error.message)
}
}
}Styling
Notifications appear in the bottom-right corner with slide-in animation.
Type Colors
| Type | Background | Icon |
|---|---|---|
| success | Green | ✓ |
| error | Red | ✕ |
| warning | Amber | ⚠ |
| info | Blue | ℹ |
Custom Animation
/* In globals.css */
@keyframes slide-in {
from {
opacity: 0;
transform: translateX(100%);
}
to {
opacity: 1;
transform: translateX(0);
}
}
.animate-slide-in {
animation: slide-in 0.3s ease-out;
}Best Practices
- Be concise - Keep titles short, use message for details
- Provide context - Explain what happened and why
- Suggest actions - Tell users what to do next
- Use appropriate types - Don’t use error for successful actions
- Consider duration - Errors should stay longer than successes
// Good
success('Vote cast!')
error('Transaction failed', 'Insufficient SOL for fees. Add ~0.01 SOL and retry.')
// Bad
success('Your vote has been successfully cast on the blockchain') // Too long
error('Error') // No context