Push уведомления

Push-уведомления представляют собой механизм отправки сообщений пользователю даже при закрытом веб-приложении. В контексте Riot.js, как и в других современных фреймворках, интеграция push-уведомлений требует взаимодействия с Service Workers, Push API и Notification API браузера.


Регистрация Service Worker

Для работы с push-уведомлениями необходим Service Worker. Он регистрируется в основном JavaScript-файле приложения:

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/service-worker.js')
    .then(registration => {
      console.log('Service Worker зарегистрирован с областью:', registration.scope);
    })
    .catch(error => {
      console.error('Ошибка регистрации Service Worker:', error);
    });
}

Ключевые моменты:

  • Service Worker должен находиться в корне сайта или соответствовать области видимости.
  • Все push-уведомления будут обрабатываться через этот Service Worker.

Подписка на Push-уведомления

После регистрации Service Worker необходимо получить разрешение от пользователя на получение уведомлений и подписаться на push-сервис:

async function subscribeUser() {
  const registration = await navigator.serviceWorker.ready;

  try {
    const subscription = await registration.pushManager.subscribe({
      userVisibleOnly: true,
      applicationServerKey: urlBase64ToUint8Array('<VAPID_PUBLIC_KEY>')
    });

    console.log('Подписка выполнена:', subscription);
    // Отправка данных подписки на сервер
    await fetch('/api/save-subscription', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(subscription)
    });
  } catch (err) {
    console.error('Ошибка подписки на Push:', err);
  }
}

Важные моменты:

  • userVisibleOnly: true гарантирует, что уведомления будут видимыми пользователю.
  • applicationServerKey — публичный ключ VAPID для безопасного взаимодействия с push-сервером.
  • Подписка должна быть сохранена на сервере для последующей отправки уведомлений.

Обработка push-событий в Service Worker

Service Worker получает push-события даже при закрытой вкладке:

self.addEventListener('push', event => {
  const data = event.data.json();
  const options = {
    body: data.body,
    icon: data.icon,
    badge: data.badge,
    data: { url: data.url }
  };

  event.waitUntil(
    self.registration.showNotification(data.title, options)
  );
});

Особенности:

  • event.data.json() позволяет извлечь данные уведомления, отправленные сервером.
  • Опции уведомления (body, icon, badge) определяют визуальное оформление.
  • data.url может использоваться для перехода на определённую страницу при клике.

Обработка кликов по уведомлениям

Для перенаправления пользователя при клике по уведомлению необходимо добавить обработчик:

self.addEventListener('notificationclick', event => {
  event.notification.close();
  event.waitUntil(
    clients.matchAll({ type: 'window', includeUncontrolled: true }).then(clientList => {
      if (clients.openWindow) {
        return clients.openWindow(event.notification.data.url);
      }
    })
  );
});

Важные моменты:

  • Закрытие уведомления (event.notification.close()) предотвращает его висение на экране.
  • clients.matchAll позволяет найти уже открытые окна и избежать создания дубликатов.

Интеграция с Riot.js компонентами

Riot.js предоставляет реактивную систему для обновления интерфейса. Можно связывать push-уведомления с состоянием компонентов:

<notification-list>
  <ul>
    <li each={notifications}>{message}</li>
  </ul>

  <script>
    export default {
      notifications: [],
      
      addNotification(data) {
        this.notifications.push(data);
        this.update();
      }
    }
  </script>
</notification-list>
  • Push-уведомление может напрямую вызывать метод addNotification через глобальный EventBus или через WebSocket-соединение.
  • Компонент автоматически обновляет интерфейс при добавлении нового уведомления.

Отправка push-уведомлений с сервера

Для отправки уведомлений сервер использует подписку пользователя и VAPID-ключи. Пример на Node.js с использованием библиотеки web-push:

const webpush = require('web-push');

webpush.setVapidDetails(
  'mailto:admin@example.com',
  '<VAPID_PUBLIC_KEY>',
  '<VAPID_PRIVATE_KEY>'
);

const pushSubscription = {/* данные подписки пользователя */};
const payload = JSON.stringify({
  title: 'Новое уведомление',
  body: 'У вас новое сообщение',
  icon: '/icon.png',
  url: '/messages'
});

webpush.sendNotification(pushSubscription, payload)
  .then(response => console.log('Уведомление отправлено', response))
  .catch(error => console.error('Ошибка отправки уведомления', error));

Особенности:

  • Payload должен быть сериализован в JSON.
  • Ошибки подписки нужно обрабатывать: пользователь мог отписаться, и данные подписки станут недействительными.

Лучшие практики

  • Всегда проверять поддержку Push API и Notification API в браузере.
  • Обрабатывать ошибки подписки и отправки уведомлений.
  • Минимизировать payload, чтобы уведомления загружались быстро.
  • Хранить подписки безопасно и обновлять их при изменении ключей или данных пользователя.

Push-уведомления в Riot.js позволяют создавать реактивные интерфейсы, которые остаются интерактивными даже при закрытом приложении, обеспечивая современный уровень взаимодействия с пользователем.