WebPushNotification.js

9.27 KB
09/11/2024 09:15
JS
WebPushNotification.js
/**
 * @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;