Add full notification preferences system

Users can now control which in-app and email notifications they receive
from their Settings tab on their channel page.

Bell (in-app) preferences:
- New comment on my video (default: on)
- Reply to my comment (default: on)
- Comment liked (default: on)
- Video liked (default: on)
- New subscriber (default: on)
- New video from channels I follow (default: on)
- New post from channels I follow (default: on)
- New user registration — super admins only (default: on)

Email preferences (same set plus):
- Comment liked (default: off — too noisy)
- Video liked (default: off — too noisy)
- New post from channels I follow (default: off)
- My video finished processing (default: on)
- Weekly activity digest every Monday (default: on)
- New user registration — super admins only (default: on)

Implementation:
- Migration: notification_preferences JSON column on users table
- User::notificationPref($key) helper with typed defaults
- All existing notification classes updated to check prefs in via()
- 4 new notification classes: NewSubscriberNotification,
  VideoLikedNotification, NewPostNotification, WeeklyDigestNotification
- 8 new email views matching existing dark theme
- SendWeeklyDigest artisan command, scheduled every Monday 09:00
- NewSubscriberNotification wired into UserController::toggleSubscribe
- VideoLikedNotification wired into UserController::toggleLike
- NewPostNotification wired into PostController::store (to all subscribers)
- Bell renderer updated for new_subscriber, video_like, new_post types
- Preferences saved via AJAX (POST /settings/notifications) — instant
  toggle with automatic revert on failure

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
ghassanandClaude Sonnet 4.6 committed 2026-05-16 23:47:28 +03:00
1 parent 3fe167e33f
commit f8d13457fa
26 files changed
+808 -18

No files matched your search

+39 -3
View File
@@ -43,6 +43,7 @@ class User extends Authenticatable implements MustVerifyEmail
'two_factor_secret',
'two_factor_enabled',
'banner',
'notification_preferences',
];
protected $hidden = [
@@ -51,11 +52,46 @@ class User extends Authenticatable implements MustVerifyEmail
];
protected $casts = [
'email_verified_at' => 'datetime',
'password' => 'hashed',
'two_factor_enabled' => 'boolean',
'email_verified_at' => 'datetime',
'password' => 'hashed',
'two_factor_enabled' => 'boolean',
'notification_preferences' => 'array',
];
// Defaults for each preference key (true = on, false = off)
private const NOTIF_DEFAULTS = [
'notif_new_comment' => true,
'notif_new_reply' => true,
'notif_comment_like' => true,
'notif_video_like' => true,
'notif_new_subscriber' => true,
'notif_new_video_from_sub' => true,
'notif_new_post_from_sub' => true,
'notif_new_user_reg' => true,
'email_new_comment' => true,
'email_new_reply' => true,
'email_comment_like' => false,
'email_video_like' => false,
'email_new_subscriber' => true,
'email_new_video_from_sub' => true,
'email_new_post_from_sub' => false,
'email_video_processed' => true,
'email_new_user_reg' => true,
'email_weekly_digest' => true,
];
public function notificationPref(string $key): bool
{
$prefs = $this->notification_preferences ?? [];
$default = self::NOTIF_DEFAULTS[$key] ?? true;
return isset($prefs[$key]) ? (bool) $prefs[$key] : $default;
}
public static function notifDefaults(): array
{
return self::NOTIF_DEFAULTS;
}
protected $appends = ['avatar_url', 'banner_url'];
// Auto-generate a unique slug-based username when creating a user without one