Notifications

Fuwafuwa Framework provides a comprehensive notification system with support for in-app notifications, toast messages, and browser push notifications.

Effective communication with users is essential for modern applications. The notification system provides multiple channels for keeping users informed—from quick toast messages that provide immediate feedback on actions, to persistent in-app notifications that users can review at their convenience, to push notifications that reach users even when they're not actively using your application.

The system is designed to be flexible and developer-friendly. Toast notifications work out of the box with zero configuration. In-app notifications integrate seamlessly with your data models. Push notifications leverage service workers for browser-native delivery. All notification types support customization to match your application's design and user experience goals.

✨ Notification Features
  • In-App Notification Center - Persistent notifications with read/unread states and filtering
  • Toast Notifications - Temporary messages for immediate feedback (success, error, warning, info)
  • Browser Push Notifications - Service Worker-based delivery for offline engagement
  • Real-Time Badges - Live notification counts on bell icons and menu items
  • User Preferences - Per-user notification settings and quiet hours

Toast Notifications

Toast notifications are brief, non-intrusive messages that provide immediate feedback about user actions or system events. They appear temporarily on screen and automatically dismiss, making them ideal for confirming successful operations, alerting users to errors, or providing quick status updates.

Unlike modal dialogs or alerts, toasts don't interrupt the user's workflow. They float above the content, deliver their message, and fade away after a few seconds. This makes them perfect for low-priority information that users should see but don't need to act on immediately. The toast system supports four types—success, error, warning, and info—each with distinctive styling and default icons.

JavaScript API

The toast notification system provides global convenience functions for displaying temporary messages. These functions are automatically available after Alpine.js initializes.

// Success notification
showSuccessToast('Record saved successfully');

// Error notification
showErrorToast('Failed to save record');

// Warning notification
showWarningToast('Please check your input');

// Info notification
showInfoToast('New updates available');

// Generic function with type
showToast('Message', 'success');

// With options
showSuccessToast('Saved!', {
  duration: 5000,     // Display duration in milliseconds (default: 5000)
  title: 'Success',   // Optional title
  autoRemove: true,   // Auto-remove after duration (default: true)
  persistent: false   // Persistent toasts won't auto-remove (default: false)
});

When to Use Toasts

Toast notifications work best for:

  • Confirming successful actions (record saved, settings updated)
  • Reporting errors that prevent an action (validation failed, save error)
  • Providing timely warnings (session expiring soon, unsaved changes)
  • Delivering informational updates (new features, maintenance notices)

Toast Options

OptionTypeDefaultDescription
durationnumber5000Display duration in milliseconds
titlestringnullOptional title displayed above the message
autoRemovebooleantrueAutomatically remove toast after duration
persistentbooleanfalseIf true, toast won't auto-remove (overrides autoRemove)

In-App Notifications

While toasts are great for immediate feedback, some information needs to persist until the user has time to review it. In-app notifications serve this purpose by storing notification records in the database that users can browse at their convenience.

The notification center provides a dedicated space for reviewing all notifications, organized by read/unread status and type. Users can mark notifications as read, delete ones they don't need, and click through to related content. Notifications can include action buttons, links to relevant pages, and rich data payloads for flexible presentation.

The notification system includes a persistent notification center accessible via the UI:

<!-- Notification bell with badge -->
<ff:notification-bell 
    :count="unreadCount" 
    @click="showNotifications = true" />

<!-- Notification panel -->
<ff:notification-panel
    x-show="showNotifications"
    :notifications="notifications"
    @mark-read="markAsRead"
    @mark-all-read="markAllAsRead"
    @delete="deleteNotification" />

Notification Model Structure

// Notification record structure
[
    'id' => 1,
    'user_login' => 'john_doe',
    'type' => 'info',      // info, success, warning, error
    'title' => 'New Message',
    'message' => 'You have received a new message',
    'action_url' => '/messages/123',
    'is_read' => 0,
    'created_at' => '2024-01-15 10:30:00'
]

Notification Types Explained

Each notification type serves a specific purpose and has distinctive visual styling:

  • Info - General information, updates, and announcements
  • Success - Confirmations of completed actions or achievements
  • Warning - Cautionary messages about potential issues or required attention
  • Error - Failed operations, errors, or blocking issues

Server-Side Notifications

While the frontend displays notifications, the backend is responsible for creating and managing notification records. The Notification model provides a clean API for creating notifications, storing them in the database, and making them available to the frontend.

Server-side notifications are typically created in response to events: a user completes an action, a background job finishes, another user mentions them, or a system event occurs. The model handles the details of database storage, user association, and notification management.

Creating Notifications

use Fuwafuwa\Model\Notification;

// Get the notification model
$notification = m(\Fuwafuwa\Model\Notification::class);

// Create a notification for a user
$notification->create(
    'john_doe',           // user_login
    'success',            // type: success, error, warning, info
    'Profile Updated',    // title
    'Your profile has been updated successfully.',  // message
    '/profile'            // action_url (optional)
);

// Mark as read
$notification->markAsRead($notificationId);

// Mark all as read for a user
$notification->markAllAsRead('john_doe');

// Get unread count
$unreadCount = $notification->getUnreadCount('john_doe');

// Get notifications for a user
$notifications = $notification->getUserNotifications('john_doe', 50);

The create() method stores a notification in the database and returns the notification instance. Use markAsRead() to mark individual notifications as read, or markAllAsRead() to clear all unread notifications for a user at once. getUnreadCount() is useful for displaying notification badges, while getUserNotifications() retrieves the list for display.

Notification Types Reference

TypeIconUse Case
successSuccessful operations
errorFailed operations
warningWarnings, cautions
infoGeneral information

Push Notifications

Browser push notifications represent the most powerful way to engage users, allowing you to deliver messages even when the user isn't actively using your application. Unlike in-app notifications that require the user to be logged in, push notifications work as long as the browser is running—even if your app is closed.

Push notifications work through the Web Push API, which uses service workers as intermediaries. Users must grant permission, and their browser registers a unique subscription with a push service. Your server sends messages to the push service, which delivers them to the user's browser. This architecture enables reliable, battery-efficient delivery without keeping connections open to your server.

Requesting Permission

Requesting Permission

// Request push notification permission
async function requestPushPermission() {
  const permission = await Notification.requestPermission();
  
  if (permission === 'granted') {
    // Subscribe to push service
    const subscription = await registerPushSubscription();
    // Send subscription to server
    await saveSubscription(subscription);
  }
}

Sending Push Notifications (Server)

use Fuwafuwa\Service\PushNotificationService;

$push = c(PushNotificationService::class);

$push->send([
    'subscription' => $userSubscription,
    'title' => 'New Message',
    'body' => 'You have a new message from John',
    'icon' => '/icon-192.png',
    'badge' => '/badge-72.png',
    'data' => ['url' => '/messages/123'],
    'actions' => [
        ['action' => 'open', 'title' => 'Open'],
        ['action' => 'dismiss', 'title' => 'Dismiss']
    ]
]);

Handling Push Events (Service Worker)

// In service worker (sw.js)
self.addEventListener('push', event => {
  const data = event.data.json();
  
  event.waitUntil(
    self.registration.showNotification(data.title, {
      body: data.body,
      icon: data.icon,
      badge: data.badge,
      data: data.data,
      actions: data.actions
    })
  );
});

// Handle notification click
self.addEventListener('notificationclick', event => {
  event.notification.close();
  
  if (event.action === 'open') {
    event.waitUntil(
      clients.openWindow(event.notification.data.url)
    );
  }
});

Push Notification Best Practices

Push notifications are powerful but can be intrusive if overused. Follow these guidelines:

  • Always ask for permission at a relevant moment, not on page load
  • Send notifications only when users have explicitly opted in
  • Include meaningful content in the notification—action buttons, deep links
  • Respect user settings and quiet hours
  • Provide value with each notification—avoid spam

Real-Time Notifications

For truly dynamic experiences, notifications should appear instantly when events occur—without the user needing to refresh the page. Real-time notification delivery achieves this through Server-Sent Events (SSE) or WebSockets, maintaining a persistent connection between the browser and server.

Server-Sent Events are simpler and ideal for one-way communication from server to client. The browser automatically reconnects if the connection drops, making SSE reliable for notification delivery. WebSockets offer bidirectional communication if you need to send messages from client to server as well, but require more complex connection management.

For real-time updates, use Server-Sent Events (SSE) or WebSocket integration:

// Client-side real-time listener
const eventSource = new EventSource('/api/notifications/stream');

eventSource.onmessage = (event) => {
  const notification = JSON.parse(event.data);
  
  // Show toast
  Toast[notification.type](notification.message);
  
  // Update notification count
  unreadCount++;
};

// Cleanup on page unload
window.addEventListener('beforeunload', () => {
  eventSource.close();
});

Real-Time Implementation Considerations

Real-time notifications increase server load due to persistent connections. Consider using a dedicated realtime service like Pusher or Ably for production applications with many users. For smaller apps, SSE with proper connection management works well. Always handle connection failures gracefully and provide fallbacks to polling when real-time connections aren't available.

User Preferences

Different users have different preferences for how and when they want to receive notifications. Some want immediate alerts for everything, others prefer a daily digest. Some work in different time zones and need quiet hours during their local night. A robust notification system respects these preferences and gives users control.

User preferences should be stored per-user and checked before sending any notification. The preferences structure includes global toggles for each notification channel (email, push, in-app) and granular controls for specific notification types. Quiet hours prevent notifications during specified times, ensuring users aren't disturbed during sleep or focus hours.

Allow users to customize their notification settings:

// Notification preferences structure
[
    'user_id' => 123,
    'email_notifications' => true,
    'push_notifications' => true,
    'in_app_notifications' => true,
    'notification_types' => [
        'order_updates' => true,
        'messages' => true,
        'system_alerts' => true,
        'marketing' => false
    ],
    'quiet_hours_start' => '22:00',
    'quiet_hours_end' => '08:00'
]

Implementing Preference Checks

Before sending notifications, check the user's preferences:

$prefs = getUserPreferences($userId);

if ($prefs['push_notifications']) {
    // Send push notification
}

if ($prefs['in_app_notifications']) {
    // Create in-app notification
}

// Check quiet hours
$now = date('H:i');
if ($now >= $prefs['quiet_hours_start'] && $now <= $prefs['quiet_hours_end']) {
    // Skip non-critical notifications
}

Database Schema

The notification system requires a database table to store notification records persistently. This table tracks all the information needed to display notifications to users, mark them as read, and manage their lifecycle. The schema supports all the features described: types, read status, action URLs, and timestamps.

The table uses user_login to associate notifications with users, is_read to track read/unread status, and action_url for deep-linking to relevant content. The type field determines visual styling and default icons for each notification.

CREATE TABLE notification (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    user_login TEXT NOT NULL,
    type VARCHAR(20) DEFAULT 'info',
    title VARCHAR(255) NOT NULL,
    message TEXT,
    action_url TEXT,
    is_read BOOLEAN DEFAULT 0,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_notification_user ON notification(user_login);
CREATE INDEX idx_notification_read ON notification(is_read);
CREATE INDEX idx_notification_created ON notification(created_at);