Files
easystream/docs/NOTIFICATIONS.md
T
Krystie f50b493df2 feat: Add comprehensive notification system with email support
- Create notification management system with VNotifications class
- Build notification API endpoints (get, mark read, delete, preferences)
- Implement notification bell UI widget with real-time polling
- Add email queue system with digest support (instant/hourly/daily/weekly)
- Create notification trigger helpers for common events
- Add fine-grained user preference controls
- Implement cron script for email processing and cleanup
- Document setup and usage in docs/NOTIFICATIONS.md

Features:
- In-app notifications with dropdown
- Email delivery with batching
- 6 notification types (comment, like, subscribe, upload, mention, system)
- User preference controls per notification type
- Lightweight polling (30s interval)
- Auto-cleanup of old notifications (90 days)
- Production-ready with proper error handling
2026-03-30 17:11:08 -07:00

9.7 KiB

Notification System

EasyStream now includes a comprehensive notification system with in-app and email delivery.

Features

  • 🔔 In-app notifications with real-time updates
  • 📧 Email notifications with digest support (instant, hourly, daily, weekly)
  • 🎯 Fine-grained preferences - users control what they receive
  • Lightweight polling - updates every 30 seconds without performance impact
  • 📱 Modern UI - notification bell with dropdown list
  • 🎨 Multiple notification types - comments, likes, subscribes, uploads, mentions, system

Setup

1. Database Migration

Run the migration to create notification tables:

docker-compose exec db mysql -u easystream -peasystream easystream < __install/migrations/002_add_notifications_system.sql

This creates:

  • db_notifications - stores all notifications
  • db_notification_preferences - user notification settings
  • db_email_queue - queued emails for batched sending

2. Add Notification Bell to Header

Include the JavaScript and add the widget container to your header template:

<!-- In your header template -->
<script src="/f_scripts/fe/js/notifications.js"></script>

<!-- Add the notification bell to your navigation -->
<div id="notification-widget"></div>

The widget will auto-initialize and start polling for updates.

3. Configure Email Sending

By default, the system uses PHP's mail() function. For production, integrate with a proper email service:

Edit f_core/f_classes/class.notifications.phpsendEmail() method to integrate:

  • SendGrid
  • Mailgun
  • Amazon SES
  • SMTP

4. Set Up Cron Job

Add a cron job to process the email queue:

# Process email queue every 15 minutes
*/15 * * * * cd /srv/easystream && php app_scripts/cron/process_notification_emails.php >> /var/log/notification-emails.log 2>&1

Usage

Triggering Notifications

Use the VNotificationTriggers helper class for common events:

require_once 'f_core/f_classes/class.notification_triggers.php';
$triggers = new VNotificationTriggers();

// When someone comments on a video
$triggers->notifyVideoComment(
    $video_owner_id,
    $commenter_id,
    $commenter_name,
    $video_id,
    $video_title,
    $comment_text
);

// When someone likes a video
$triggers->notifyVideoLike(
    $video_owner_id,
    $liker_id,
    $liker_name,
    $video_id,
    $video_title
);

// When someone subscribes
$triggers->notifySubscribe(
    $channel_owner_id,
    $subscriber_id,
    $subscriber_name
);

// When uploading a new video (notifies all subscribers)
$triggers->notifySubscribersNewVideo(
    $channel_owner_id,
    $channel_name,
    $video_id,
    $video_title
);

// When mentioning a user in a comment
$triggers->notifyMention(
    $mentioned_user_id,
    $mentioner_id,
    $mentioner_name,
    $video_id,
    $comment_text
);

// System notification
$triggers->notifySystem(
    $user_id,
    'Your video was approved',
    'Your video "Tutorial" is now live!',
    '/watch?v=123'
);

API Endpoints

Get Notifications

GET /api/notifications.php?limit=20&offset=0&unread_only=true

Response:

{
  "success": true,
  "data": {
    "notifications": [
      {
        "notification_id": 123,
        "type": "comment",
        "title": "John commented on your video",
        "message": "Great tutorial!",
        "link": "/watch?v=456#comments",
        "is_read": 0,
        "created_at": "2026-03-30 17:30:00",
        "actor_username": "john",
        "actor_avatar": "/avatars/john.jpg"
      }
    ],
    "unread_count": 5
  }
}

Get Unread Count (Lightweight)

GET /api/notifications_count.php

Response:

{
  "success": true,
  "unread_count": 5
}

Mark as Read

POST /api/notifications.php?action=mark_read
Content-Type: application/json

{"id": 123}

Or mark multiple:

{"id": "123,124,125"}

Mark All as Read

POST /api/notifications.php?action=mark_all_read

Delete Notification

DELETE /api/notifications.php?id=123

User Preferences

Get Preferences

GET /api/notification_preferences.php

Response:

{
  "success": true,
  "data": {
    "email_comments": 1,
    "email_likes": 1,
    "email_subscribes": 1,
    "email_uploads": 1,
    "email_mentions": 1,
    "email_digest": "instant",
    "push_enabled": 1,
    "push_comments": 1,
    "push_likes": 1,
    "push_subscribes": 1
  }
}

Update Preferences

POST /api/notification_preferences.php
Content-Type: application/json

{
  "email_digest": "daily",
  "email_likes": 0,
  "push_comments": 1
}

Email Digest Modes

Users can choose how often they receive emails:

  • instant - Send immediately when notification is created
  • hourly - Batch and send once per hour
  • daily - Send once per day (at midnight)
  • weekly - Send once per week (Monday)
  • never - No email notifications

Notification Types

Type Description Example
comment New comment on video or reply to comment "John commented on your video"
like Someone liked a video "Sarah liked your video"
subscribe New channel subscriber "Mike subscribed to your channel"
video_upload Subscribed channel uploaded new video "TechChannel uploaded a new video"
mention User mentioned in a comment "@username mentioned you"
system Admin/system messages "Your video was approved"

Frontend Integration

The notification widget provides:

  • Real-time bell icon with unread count badge
  • Dropdown with last 20 notifications
  • Mark individual or all as read
  • Auto-polling every 30 seconds
  • Click notification to navigate and mark as read

JavaScript API

// Access the widget instance
const widget = window.notificationWidget;

// Manually fetch notifications
widget.fetchNotifications();

// Manually fetch unread count
widget.fetchUnreadCount();

// Change poll interval (milliseconds)
widget.pollInterval = 60000; // 1 minute
widget.stopPolling();
widget.startPolling();

// Programmatically mark as read
widget.markAsRead(notification_id);

// Mark all as read
widget.markAllAsRead();

Database Schema

db_notifications

Column Type Description
notification_id INT Primary key
usr_id INT User receiving notification
type ENUM Notification type
title VARCHAR(255) Short title
message TEXT Optional detailed message
link VARCHAR(512) URL to navigate to
actor_id INT User who triggered notification
related_video_id INT Associated video
related_comment_id INT Associated comment
is_read TINYINT Read status
is_seen TINYINT Seen status (viewed in UI)
email_sent TINYINT Email delivery status
created_at DATETIME Creation timestamp
read_at DATETIME When marked as read

db_notification_preferences

Column Type Description
usr_id INT User ID (unique)
email_comments TINYINT Email for comments
email_likes TINYINT Email for likes
email_subscribes TINYINT Email for subscribes
email_uploads TINYINT Email for new uploads
email_mentions TINYINT Email for mentions
email_digest ENUM Digest frequency
push_enabled TINYINT Push notifications enabled
push_comments TINYINT Push for comments
push_likes TINYINT Push for likes
push_subscribes TINYINT Push for subscribes

db_email_queue

Column Type Description
queue_id INT Primary key
usr_id INT Recipient user
email VARCHAR(255) Email address
subject VARCHAR(255) Email subject
body TEXT Email HTML body
scheduled_for DATETIME When to send
sent_at DATETIME Actual send time
attempts INT Send attempts
last_error TEXT Last error message

Performance

  • Polling: Lightweight count query every 30s (~1-2ms)
  • Dropdown: Full notification list only when opened
  • Email queue: Batched processing reduces database load
  • Auto-cleanup: Old read notifications purged after 90 days

Cleanup

The cron script automatically cleans up:

  • Read notifications older than 90 days
  • Sent emails from queue

Adjust retention in app_scripts/cron/process_notification_emails.php:

$notifications->cleanup(90); // Days to keep

Troubleshooting

Notifications not appearing

  1. Check database tables exist
  2. Verify JavaScript is loaded: window.notificationWidget
  3. Check browser console for errors
  4. Verify user is authenticated (session active)

Emails not sending

  1. Check cron job is running
  2. View email queue: SELECT * FROM db_email_queue WHERE sent_at IS NULL
  3. Check email configuration in class.notifications.php
  4. View logs: docker-compose logs php | grep email

High polling load

Increase poll interval in the frontend:

// In notifications.js
this.pollInterval = 60000; // 60 seconds instead of 30

Future Enhancements

  • WebSocket/SSE for true real-time updates (eliminate polling)
  • Push notifications (web push API)
  • Mobile app push notifications (FCM/APNS)
  • Notification grouping (e.g., "5 people liked your video")
  • Rich notification preview with thumbnails
  • Notification filtering/search
  • Mark as unread
  • Snooze notifications

Learn More