## ✅ Push Notification Settings - Fully Functional ### Backend (PHP) **PushNotificationHandler Updates:** - Added `SETTINGS_KEY` constant - `ensure_default_settings()` - Initialize defaults - `get_default_settings()` - Return default config - `get_settings()` - Fetch current settings - `update_settings()` - Save settings **Default Settings:** ```php [ 'use_logo' => true, 'use_product_images' => true, 'use_gravatar' => false, 'click_action' => '/wp-admin/admin.php?page=woonoow#/orders', 'require_interaction' => false, 'silent' => false, ] ``` **NotificationsController:** - `GET /notifications/push/settings` - Fetch settings - `POST /notifications/push/settings` - Update settings - Permission-protected endpoints ### Frontend (React) **ChannelConfig Component:** - Fetches push settings on open - Real-time state management - Connected switches and inputs - Save mutation with loading state - Toast notifications for success/error - Disabled state during save **Settings Available:** 1. **Branding** - Use Store Logo - Use Product Images - Use Customer Gravatar 2. **Behavior** - Click Action URL (input) - Require Interaction - Silent Notifications ### Features ✅ **Backend Storage** - Settings saved in wp_options ✅ **REST API** - GET and POST endpoints ✅ **Frontend UI** - Full CRUD interface ✅ **State Management** - React Query integration ✅ **Loading States** - Skeleton and button states ✅ **Error Handling** - Toast notifications ✅ **Default Values** - Sensible defaults --- **Next: Email channel toggle** 📧
282 lines
6.4 KiB
PHP
282 lines
6.4 KiB
PHP
<?php
|
|
/**
|
|
* Push Notification Handler
|
|
*
|
|
* Handles browser push notifications for PWA.
|
|
*
|
|
* @package WooNooW\Core\Notifications
|
|
*/
|
|
|
|
namespace WooNooW\Core\Notifications;
|
|
|
|
class PushNotificationHandler {
|
|
|
|
/**
|
|
* Option key for storing subscriptions
|
|
*/
|
|
const SUBSCRIPTIONS_KEY = 'woonoow_push_subscriptions';
|
|
|
|
/**
|
|
* Option key for VAPID keys
|
|
*/
|
|
const VAPID_KEYS_KEY = 'woonoow_push_vapid_keys';
|
|
|
|
/**
|
|
* Option key for push settings
|
|
*/
|
|
const SETTINGS_KEY = 'woonoow_push_settings';
|
|
|
|
/**
|
|
* Initialize push notifications
|
|
*/
|
|
public static function init() {
|
|
// Generate VAPID keys if not exists
|
|
self::ensure_vapid_keys();
|
|
|
|
// Ensure default settings exist
|
|
self::ensure_default_settings();
|
|
}
|
|
|
|
/**
|
|
* Ensure default push settings exist
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function ensure_default_settings() {
|
|
$settings = get_option(self::SETTINGS_KEY);
|
|
|
|
if (!$settings) {
|
|
$settings = self::get_default_settings();
|
|
update_option(self::SETTINGS_KEY, $settings);
|
|
}
|
|
|
|
return $settings;
|
|
}
|
|
|
|
/**
|
|
* Get default push settings
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function get_default_settings() {
|
|
return [
|
|
'use_logo' => true,
|
|
'use_product_images' => true,
|
|
'use_gravatar' => false,
|
|
'click_action' => '/wp-admin/admin.php?page=woonoow#/orders',
|
|
'require_interaction' => false,
|
|
'silent' => false,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get push settings
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function get_settings() {
|
|
$settings = get_option(self::SETTINGS_KEY);
|
|
|
|
if (!$settings) {
|
|
$settings = self::get_default_settings();
|
|
}
|
|
|
|
return $settings;
|
|
}
|
|
|
|
/**
|
|
* Update push settings
|
|
*
|
|
* @param array $settings
|
|
* @return bool
|
|
*/
|
|
public static function update_settings($settings) {
|
|
$current = self::get_settings();
|
|
$updated = array_merge($current, $settings);
|
|
|
|
return update_option(self::SETTINGS_KEY, $updated);
|
|
}
|
|
|
|
/**
|
|
* Ensure VAPID keys exist
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function ensure_vapid_keys() {
|
|
$keys = get_option(self::VAPID_KEYS_KEY);
|
|
|
|
if (!$keys || !isset($keys['public_key']) || !isset($keys['private_key'])) {
|
|
// Generate new VAPID keys
|
|
$keys = self::generate_vapid_keys();
|
|
update_option(self::VAPID_KEYS_KEY, $keys);
|
|
}
|
|
|
|
return $keys;
|
|
}
|
|
|
|
/**
|
|
* Generate VAPID keys
|
|
*
|
|
* @return array
|
|
*/
|
|
private static function generate_vapid_keys() {
|
|
// For now, use placeholder keys
|
|
// In production, use web-push library or external service
|
|
return [
|
|
'public_key' => base64_encode(random_bytes(65)),
|
|
'private_key' => base64_encode(random_bytes(32)),
|
|
'generated_at' => current_time('mysql'),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get VAPID public key
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function get_public_key() {
|
|
$keys = self::ensure_vapid_keys();
|
|
return $keys['public_key'];
|
|
}
|
|
|
|
/**
|
|
* Subscribe to push notifications
|
|
*
|
|
* @param array $subscription Subscription data
|
|
* @param int $user_id User ID
|
|
* @return bool
|
|
*/
|
|
public static function subscribe($subscription, $user_id = 0) {
|
|
$subscriptions = get_option(self::SUBSCRIPTIONS_KEY, []);
|
|
|
|
$subscription_id = md5(json_encode($subscription));
|
|
|
|
$subscriptions[$subscription_id] = [
|
|
'subscription' => $subscription,
|
|
'user_id' => $user_id,
|
|
'subscribed_at' => current_time('mysql'),
|
|
];
|
|
|
|
return update_option(self::SUBSCRIPTIONS_KEY, $subscriptions);
|
|
}
|
|
|
|
/**
|
|
* Unsubscribe from push notifications
|
|
*
|
|
* @param string $subscription_id Subscription ID
|
|
* @return bool
|
|
*/
|
|
public static function unsubscribe($subscription_id) {
|
|
$subscriptions = get_option(self::SUBSCRIPTIONS_KEY, []);
|
|
|
|
if (isset($subscriptions[$subscription_id])) {
|
|
unset($subscriptions[$subscription_id]);
|
|
return update_option(self::SUBSCRIPTIONS_KEY, $subscriptions);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Get all subscriptions
|
|
*
|
|
* @param int $user_id Optional user ID filter
|
|
* @return array
|
|
*/
|
|
public static function get_subscriptions($user_id = null) {
|
|
$subscriptions = get_option(self::SUBSCRIPTIONS_KEY, []);
|
|
|
|
if ($user_id !== null) {
|
|
return array_filter($subscriptions, function($sub) use ($user_id) {
|
|
return $sub['user_id'] == $user_id;
|
|
});
|
|
}
|
|
|
|
return $subscriptions;
|
|
}
|
|
|
|
/**
|
|
* Send push notification
|
|
*
|
|
* @param string $title Notification title
|
|
* @param string $body Notification body
|
|
* @param array $options Additional options
|
|
* @param int $user_id Optional user ID to target
|
|
* @return int Number of notifications sent
|
|
*/
|
|
public static function send($title, $body, $options = [], $user_id = null) {
|
|
$subscriptions = self::get_subscriptions($user_id);
|
|
|
|
if (empty($subscriptions)) {
|
|
return 0;
|
|
}
|
|
|
|
$payload = [
|
|
'title' => $title,
|
|
'body' => $body,
|
|
'icon' => $options['icon'] ?? get_site_icon_url(),
|
|
'badge' => $options['badge'] ?? get_site_icon_url(),
|
|
'data' => $options['data'] ?? [],
|
|
'actions' => $options['actions'] ?? [],
|
|
];
|
|
|
|
$sent = 0;
|
|
|
|
foreach ($subscriptions as $subscription_id => $sub) {
|
|
try {
|
|
// In production, use web-push library
|
|
// For now, store in queue for service worker to fetch
|
|
self::queue_notification($subscription_id, $payload);
|
|
$sent++;
|
|
} catch (\Exception $e) {
|
|
error_log('Push notification error: ' . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
return $sent;
|
|
}
|
|
|
|
/**
|
|
* Queue notification for delivery
|
|
*
|
|
* @param string $subscription_id Subscription ID
|
|
* @param array $payload Notification payload
|
|
*/
|
|
private static function queue_notification($subscription_id, $payload) {
|
|
$queue = get_option('woonoow_push_queue', []);
|
|
|
|
$queue[] = [
|
|
'subscription_id' => $subscription_id,
|
|
'payload' => $payload,
|
|
'queued_at' => current_time('mysql'),
|
|
];
|
|
|
|
update_option('woonoow_push_queue', $queue);
|
|
}
|
|
|
|
/**
|
|
* Get queued notifications for user
|
|
*
|
|
* @param int $user_id User ID
|
|
* @return array
|
|
*/
|
|
public static function get_queued_notifications($user_id) {
|
|
$queue = get_option('woonoow_push_queue', []);
|
|
$subscriptions = self::get_subscriptions($user_id);
|
|
$subscription_ids = array_keys($subscriptions);
|
|
|
|
$user_notifications = array_filter($queue, function($item) use ($subscription_ids) {
|
|
return in_array($item['subscription_id'], $subscription_ids);
|
|
});
|
|
|
|
// Clear delivered notifications
|
|
$remaining = array_filter($queue, function($item) use ($subscription_ids) {
|
|
return !in_array($item['subscription_id'], $subscription_ids);
|
|
});
|
|
|
|
update_option('woonoow_push_queue', $remaining);
|
|
|
|
return array_values($user_notifications);
|
|
}
|
|
}
|