/** * @class WebPushNotification * @description จัดการการแจ้งเตือนแบบ Web Push */ class WebPushNotification { static subscriptions = new Set(); static permission = null; static hasServiceWorker = false; /** * @static * @method initialize * @description ตั้งค่าเริ่มต้นสำหรับ Web Push */ static async initialize() { try { // ตรวจสอบการรองรับ if (!('Notification' in window)) { throw new Error('เบราว์เซอร์นี้ไม่รองรับการแจ้งเตือนแบบ Web Push'); } // ตรวจสอบและขอสิทธิ์ this.permission = await Notification.requestPermission(); if (this.permission === 'granted') { // พยายามลงทะเบียน Service Worker แต่ไม่บังคับ try { await this.registerServiceWorker(); } catch (error) { console.warn('ไม่สามารถลงทะเบียน Service Worker:', error); // ทำงานต่อไปแม้ไม่มี Service Worker } } } catch (error) { console.error('ไม่สามารถเริ่มต้น Web Push:', error); } } /** * @static * @method registerServiceWorker * @description ลงทะเบียน Service Worker สำหรับ Web Push (ถ้าเป็นไปได้) */ static async registerServiceWorker() { if (!('serviceWorker' in navigator)) { return; } try { // ใช้ Blob สร้าง Service Worker แทนการโหลดไฟล์ const swContent = ` self.addEventListener('push', function(event) { if (!(self.Notification && self.Notification.permission === 'granted')) { return; } const data = event.data?.json() ?? {}; const title = data.title || 'การแจ้งเตือนใหม่'; const options = { body: data.message, icon: data.icon, badge: data.badge, vibrate: data.vibrate, data: data.data || {}, actions: data.actions || [], requireInteraction: data.requireInteraction || false }; event.waitUntil( self.registration.showNotification(title, options) ); }); self.addEventListener('notificationclick', function(event) { event.notification.close(); if (event.action === 'view' && event.notification.data.url) { event.waitUntil( clients.openWindow(event.notification.data.url) ); } }); self.addEventListener('notificationclose', function(event) { console.log('Notification closed', event.notification); }); `; const blob = new Blob([swContent], {type: 'text/javascript'}); const swUrl = URL.createObjectURL(blob); const registration = await navigator.serviceWorker.register(swUrl, { scope: './' }); this.hasServiceWorker = true; // ถ้าต้องการใช้ Push API if (CONFIG.NOTIFICATIONS.WEB_PUSH.config.vapidKeys?.public) { const subscription = await registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: CONFIG.NOTIFICATIONS.WEB_PUSH.config.vapidKeys.public }); this.subscriptions.add(subscription); } // ทำความสะอาด URL object URL.revokeObjectURL(swUrl); } catch (error) { console.warn('Service Worker registration failed:', error); this.hasServiceWorker = false; } } /** * @static * @method show * @description แสดงการแจ้งเตือนโดยใช้ Native Notification API * @param {string} title - หัวข้อการแจ้งเตือน * @param {Object} options - ตัวเลือกการแจ้งเตือน */ static async show(title, options = {}) { try { if (this.permission !== 'granted') { // ลองขอสิทธิ์อีกครั้ง this.permission = await Notification.requestPermission(); if (this.permission !== 'granted') { throw new Error('ไม่ได้รับอนุญาตให้แสดงการแจ้งเตือน'); } } // รวมตัวเลือกกับค่าเริ่มต้น const defaultOptions = CONFIG.NOTIFICATIONS.WEB_PUSH.config.options; const notificationOptions = { ...defaultOptions, ...options, badge: defaultOptions.badge, timestamp: Date.now(), silent: false }; // สร้างการแจ้งเตือน const notification = new Notification(title, notificationOptions); // จัดการ events notification.onclick = (event) => { event.preventDefault(); if (options.onClick) { options.onClick(event); } notification.close(); }; notification.onclose = (event) => { if (options.onClose) { options.onClose(event); } }; // ปิดอัตโนมัติ if (options.duration) { setTimeout(() => notification.close(), options.duration); } return notification; } catch (error) { console.error('ไม่สามารถแสดงการแจ้งเตือน:', error); throw error; } } /** * @static * @method info * @description แสดงการแจ้งเตือนแบบ info * @param {string} message - ข้อความ * @param {Object} options - ตัวเลือกเพิ่มเติม */ static info(message, options = {}) { const typeConfig = CONFIG.NOTIFICATION_SETTINGS.types.info; return this.show(message, { ...options, icon: typeConfig.icon, badge: CONFIG.NOTIFICATIONS.WEB_PUSH.config.options.badge, duration: CONFIG.NOTIFICATION_SETTINGS.display.duration }); } /** * @static * @method success * @description แสดงการแจ้งเตือนแบบ success * @param {string} message - ข้อความ * @param {Object} options - ตัวเลือกเพิ่มเติม */ static success(message, options = {}) { const typeConfig = CONFIG.NOTIFICATION_SETTINGS.types.success; return this.show(message, { ...options, icon: typeConfig.icon, badge: CONFIG.NOTIFICATIONS.WEB_PUSH.config.options.badge, duration: CONFIG.NOTIFICATION_SETTINGS.display.duration }); } /** * @static * @method warning * @description แสดงการแจ้งเตือนแบบ warning * @param {string} message - ข้อความ * @param {Object} options - ตัวเลือกเพิ่มเติม */ static warning(message, options = {}) { const typeConfig = CONFIG.NOTIFICATION_SETTINGS.types.warning; return this.show(message, { ...options, icon: typeConfig.icon, badge: CONFIG.NOTIFICATIONS.WEB_PUSH.config.options.badge, duration: CONFIG.NOTIFICATION_SETTINGS.display.duration * 1.5 // แสดงนานขึ้น }); } /** * @static * @method error * @description แสดงการแจ้งเตือนแบบ error * @param {string} message - ข้อความ * @param {Object} options - ตัวเลือกเพิ่มเติม */ static error(message, options = {}) { const typeConfig = CONFIG.NOTIFICATION_SETTINGS.types.error; return this.show(message, { ...options, icon: typeConfig.icon, badge: CONFIG.NOTIFICATIONS.WEB_PUSH.config.options.badge, duration: CONFIG.NOTIFICATION_SETTINGS.display.duration * 2, // แสดงนานขึ้น requireInteraction: true // ต้องการการตอบสนองจากผู้ใช้ }); } /** * @static * @method urgent * @description แสดงการแจ้งเตือนแบบ urgent * @param {string} message - ข้อความ * @param {Object} options - ตัวเลือกเพิ่มเติม */ static urgent(message, options = {}) { const typeConfig = CONFIG.NOTIFICATION_SETTINGS.types.urgent; return this.show(message, { ...options, icon: typeConfig.icon, badge: CONFIG.NOTIFICATIONS.WEB_PUSH.config.options.badge, vibrate: [200, 100, 200, 100, 200], // รูปแบบการสั่น requireInteraction: true, actions: [ { action: 'view', title: 'ดูรายละเอียด' }, { action: 'dismiss', title: 'ปิด' } ] }); } } // เริ่มต้นระบบเมื่อโหลดหน้าเว็บ if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => WebPushNotification.initialize()); } else { WebPushNotification.initialize(); } window.WebPushNotification = new WebPushNotification;