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

TypeBackgroundIcon
successGreen
errorRed
warningAmber
infoBlue

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

  1. Be concise - Keep titles short, use message for details
  2. Provide context - Explain what happened and why
  3. Suggest actions - Tell users what to do next
  4. Use appropriate types - Don’t use error for successful actions
  5. 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