Добавление push-уведомлений в веб-приложение

Push-уведомления — это простой и эффективный способ повторного взаимодействия с пользователями. В этой практической работе вы узнаете, как добавить push-уведомления в своё веб-приложение.

Чему вы научитесь

  • Как подписать и отписать пользователя на push-уведомления
  • Как обрабатывать входящие push-сообщения
  • Как отобразить уведомление
  • Как реагировать на клики по уведомлениям

Что вам понадобится

  • Chrome 52 или выше
  • Веб-сервер для Chrome или ваш собственный веб-сервер по выбору
  • Текстовый редактор
  • Базовые знания HTML, CSS, JavaScript и Chrome DevTools
  • Пример кода (см. раздел «Настройка»).

Загрузите пример кода

У вас есть два варианта получения примера кода для этой лабораторной работы:

  • Клонируйте репозиторий Git:
git clone https://github.com/GoogleChrome/push-notifications.git
  • Загрузите ZIP-файл:

Загрузить исходный код

Если вы загрузите исходный код в виде ZIP-файла, то при его распаковке вы получите корневую папку push-notifications-master .

Установите и проверьте веб-сервер

Хотя вы можете использовать свой собственный веб-сервер, эта лабораторная работа разработана для работы с приложением «Веб-сервер для Chrome». Если у вас ещё не установлено это приложение, вы можете скачать его из интернет-магазина Chrome:

Установить веб-сервер для Chrome

После установки приложения «Веб-сервер для Chrome» нажмите на ярлык «Приложения» на панели закладок:

В окне «Приложения» щелкните значок «Веб-сервер»:

Далее вы увидите диалоговое окно, позволяющее настроить локальный веб-сервер:

Нажмите кнопку «Выбрать папку» и выберите папку app в скачанной папке push-notifications . Это позволит вам предоставлять доступ к текущей работе по URL-адресу, указанному в разделе «URL-адрес(а) веб-сервера» диалогового окна.

В разделе «Параметры» установите флажок рядом с опцией «Автоматически показывать index.html» , как показано ниже:

Затем остановите и перезапустите сервер, сдвинув переключатель Веб-сервер: ЗАПУЩЕН влево, а затем обратно вправо.

Нажмите на URL-адрес веб-сервера, чтобы открыть свой сайт в браузере. Вы увидите страницу, похожую на эту, хотя в вашей версии адрес может быть 127.0.0.1:8887:

00-push-codelab.png

Всегда обновляйте Service Worker

Во время разработки полезно следить за тем, чтобы ваш Service Worker всегда был обновлен и содержал последние изменения.

Чтобы настроить это в Chrome:

  1. Перейдите на вкладку Push Codelab .
  2. Откройте DevTools: Ctrl-Shift-I в Windows и Linux, Cmd-Option-I в macOS.
  3. Выберите панель «Приложение» , перейдите на вкладку «Сервисные работники» и установите флажок «Обновлять при перезагрузке» . Если этот флажок установлен, сервисный работник принудительно обновляется при каждой перезагрузке страницы.

Завершенный код

В каталоге вашего app обратите внимание на пустой файл sw.js Этот файл будет вашим сервис-воркером. Пока он может оставаться пустым. Вы добавите в него код позже.

Для начала вам необходимо зарегистрировать этот файл как ваш service worker.

Ваша страница app/index.html загружает scripts/main.js . Вы регистрируете свой сервис-воркер в этом JavaScript-файле.

Добавьте следующий код в scripts/main.js :

if ('serviceWorker' in navigator && 'PushManager' in window) {
  console.log('Service Worker and Push are supported');

  navigator.serviceWorker.register('sw.js')
  .then(function(swReg) {
    console.log('Service Worker is registered', swReg);

    swRegistration = swReg;
  })
  .catch(function(error) {
    console.error('Service Worker Error', error);
  });
} else {
  console.warn('Push messaging is not supported');
  pushButton.textContent = 'Push Not Supported';
}

Этот код проверяет, поддерживаются ли вашим браузером сервис-воркеры и push-уведомления. Если они поддерживаются, код регистрирует ваш файл sw.js

Попробуйте это

Проверьте изменения, обновив вкладку Push Codelab в браузере.

Проверьте консоль в Chrome DevTools на наличие Service Worker is registered message , например:

Получить ключи сервера приложений

Для работы с этой лабораторной работой вам необходимо сгенерировать ключи сервера приложений. Это можно сделать на сайте-компаньоне: web-push-codelab.glitch.me

Здесь вы можете сгенерировать пару открытого и закрытого ключей.

push-codelab-04-companion.png

Скопируйте свой открытый ключ в scripts/main.js заменив значение <Your Public Key> :

const applicationServerPublicKey = '<Your Public Key>';

Важно: никогда не помещайте свой закрытый ключ в веб-приложение!

Завершенный код

В настоящее время кнопка «Включить» в веб-приложении отключена и не может быть нажата. Это связано с тем, что рекомендуется отключать кнопку push-уведомлений по умолчанию и включать её только после того, как вы убедитесь, что браузер поддерживает push-уведомления, и сможете проверить, подписан ли пользователь на рассылку.

Вам необходимо создать две функции в scripts/main.js :

  • initializeUI , чтобы проверить, подписан ли пользователь в данный момент
  • updateBtn , чтобы включить кнопку и изменить текст в зависимости от того, подписан ли пользователь или нет

Добавьте функцию initializeUI в main.js следующим образом:

function initializeUI() {
  // Set the initial subscription value
  swRegistration.pushManager.getSubscription()
  .then(function(subscription) {
    isSubscribed = !(subscription === null);

    if (isSubscribed) {
      console.log('User IS subscribed.');
    } else {
      console.log('User is NOT subscribed.');
    }

    updateBtn();
  });
}

Ваш новый метод использует swRegistration из предыдущего шага, получает из него свойство pushManager и вызывает для него getSubscription() .

pushManager . getSubscription() возвращает обещание, которое разрешается с текущей подпиской, если она есть. В противном случае возвращается null . Таким образом, вы можете проверить, подписан ли пользователь, установить значение isSubscribed и затем вызвать updateBtn() для обновления кнопки.

Добавьте функцию updateBtn() в main.js :

function updateBtn() {
  if (isSubscribed) {
    pushButton.textContent = 'Disable Push Messaging';
  } else {
    pushButton.textContent = 'Enable Push Messaging';
  }

  pushButton.disabled = false;
}

Эта функция включает кнопку и изменяет текст кнопки в зависимости от того, подписан ли пользователь или нет.

Последнее, что нужно сделать, это вызвать initializeUI() когда ваш service worker зарегистрирован в main.js :

navigator.serviceWorker.register('sw.js')
.then(function(swReg) {
  console.log('Service Worker is registered', swReg);

  swRegistration = swReg;
  initializeUI();
})

Попробуйте это

Обновите вкладку Push Codelab . Вы увидите, что кнопка «Включить push-сообщения» теперь активна (ее можно нажать), а в консоли должно появиться сообщение User is NOT subscribed .

По мере выполнения оставшейся части этой лабораторной работы вы увидите, что текст кнопки меняется каждый раз, когда вы подписываетесь или отменяете подписку.

Завершенный код

В данный момент кнопка «Включить push-уведомления» не работает. Давайте это исправим.

В функции initializeUI() добавьте прослушиватель щелчков для вашей кнопки:

function initializeUI() {
  pushButton.addEventListener('click', function() {
    pushButton.disabled = true;
    if (isSubscribed) {
      // TODO: Unsubscribe user
    } else {
      subscribeUser();
    }
  });

  // Set the initial subscription value
  swRegistration.pushManager.getSubscription()
  .then(function(subscription) {
    isSubscribed = !(subscription === null);

    updateSubscriptionOnServer(subscription);

    if (isSubscribed) {
      console.log('User IS subscribed.');
    } else {
      console.log('User is NOT subscribed.');
    }

    updateBtn();
  });
}

Когда пользователь нажимает кнопку, вы отключаете ее, просто чтобы убедиться, что пользователь не сможет нажать ее второй раз, поскольку подписка на push-уведомления может занять некоторое время.

Затем вы вызываете subscribeUser() , если пользователь ещё не подписан. Для этого вам нужно вставить следующий код в scripts/main.js :

function subscribeUser() {
  const applicationServerKey = urlB64ToUint8Array(applicationServerPublicKey);
  swRegistration.pushManager.subscribe({
    userVisibleOnly: true,
    applicationServerKey: applicationServerKey
  })
  .then(function(subscription) {
    console.log('User is subscribed.');

    updateSubscriptionOnServer(subscription);

    isSubscribed = true;

    updateBtn();
  })
  .catch(function(error) {
    console.error('Failed to subscribe the user: ', error);
    updateBtn();
  });
}

Давайте подробно рассмотрим, что делает этот код и как он подписывает пользователя на push-сообщения.

Сначала вы берёте открытый ключ сервера приложений, закодированный в формате Base64 URL-безопасно , и преобразуете его в массив UInt8Array , поскольку это ожидаемые входные данные для вызова subscribe() . Функция urlB64ToUint8Array() находится в начале файла scripts/main.js .

После преобразования значения вы вызываете метод subscribe() в pushManager вашего service worker, передавая открытый ключ вашего сервера приложений и значение userVisibleOnly: true .

const applicationServerKey = urlB64ToUint8Array(applicationServerPublicKey);
swRegistration.pushManager.subscribe({
  userVisibleOnly: true,
  applicationServerKey: applicationServerKey
})

Параметр userVisibleOnly гарантирует, что вы будете показывать уведомление при каждой отправке push-сообщения. В настоящее время это значение является обязательным и должно быть равно true.

Вызов subscribe() возвращает обещание, которое будет разрешено после следующих шагов:

  1. Пользователь дал разрешение на отображение уведомлений.
  2. Браузер отправил сетевой запрос в push-службу, чтобы получить данные, необходимые для создания PushSubscription .

Если эти шаги выполнены успешно, обещание subscribe() разрешится с помощью PushSubscription . Если пользователь не даст разрешения или возникнут проблемы с подпиской, обещание будет отклонено с ошибкой. Это даст вам следующую цепочку обещаний в вашей кодовой лаборатории:

swRegistration.pushManager.subscribe({
  userVisibleOnly: true,
  applicationServerKey: applicationServerKey
})
.then(function(subscription) {
  console.log('User is subscribed.');

  updateSubscriptionOnServer(subscription);

  isSubscribed = true;

  updateBtn();

})
.catch(function(err) {
  console.log('Failed to subscribe the user: ', err);
  updateBtn();
});

При этом вы либо получаете подписку и считаете пользователя подписанным, либо перехватываете ошибку и выводите её в консоль. В обоих случаях вы вызываете updateBtn() , чтобы убедиться, что кнопка снова включена и имеет соответствующий текст.

В реальном приложении функция updateSubscriptionOnServer() отправляет данные о подписке на бэкенд, но в рамках лабораторной работы вы просто отображаете подписку в пользовательском интерфейсе. Добавьте следующую функцию в scripts/main.js :

function updateSubscriptionOnServer(subscription) {
  // TODO: Send subscription to application server

  const subscriptionJson = document.querySelector('.js-subscription-json');
  const subscriptionDetails =
    document.querySelector('.js-subscription-details');

  if (subscription) {
    subscriptionJson.textContent = JSON.stringify(subscription);
    subscriptionDetails.classList.remove('is-invisible');
  } else {
    subscriptionDetails.classList.add('is-invisible');
  }
}

Попробуйте это

Перейдите на вкладку «Push Codelab» , обновите страницу и нажмите кнопку. Вы увидите запрос на разрешение, подобный следующему:

Если вы предоставите разрешение, вы увидите User is subscribed в консоли. Текст кнопки изменится на «Отключить push-сообщения» , и вы сможете просмотреть подписку в формате JSON в нижней части страницы.

Завершенный код

Один из вопросов, который вы пока не проработали, — это что произойдёт, если пользователь заблокирует запрос на разрешение. Этот вопрос требует особого внимания, поскольку, если пользователь заблокирует разрешение, ваше веб-приложение не сможет повторно отображать запрос на разрешение и не сможет подписать пользователя. Вам нужно как минимум отключить кнопку, чтобы пользователь знал, что её нельзя использовать.

Очевидный способ обработки этого сценария — функция updateBtn() . Достаточно просто проверить значение Notification.permission , например:

function updateBtn() {
  if (Notification.permission === 'denied') {
    pushButton.textContent = 'Push Messaging Blocked';
    pushButton.disabled = true;
    updateSubscriptionOnServer(null);
    return;
  }

  if (isSubscribed) {
    pushButton.textContent = 'Disable Push Messaging';
  } else {
    pushButton.textContent = 'Enable Push Messaging';
  }

  pushButton.disabled = false;
}

Вы знаете, что если в разрешении denied , то пользователь не может быть подписан, и вы больше ничего не можете сделать, поэтому наилучшим подходом будет постоянное отключение кнопки.

Попробуйте это

Поскольку вы уже предоставили разрешение для своего веб-приложения на предыдущем шаге, вам нужно нажать на i в кружке в адресной строке и изменить разрешение «Уведомления» на «Использовать глобальное значение по умолчанию (Спросить)» .

После изменения этой настройки обновите страницу, нажмите кнопку «Включить push-сообщения» и выберите «Заблокировать» в диалоговом окне разрешений. Кнопка будет отключена, и на ней отобразится текст «Push-сообщения заблокированы» .

Благодаря этому изменению теперь вы можете подписать пользователя, позаботившись о возможных сценариях разрешений.

Завершенный код

Прежде чем вы узнаете, как отправлять push-сообщения из своего бэкэнда, вам нужно рассмотреть, что на самом деле произойдет, когда подписанный пользователь получит push-сообщение.

При запуске push-сообщения браузер получает его, определяет, для какого сервис-воркера оно предназначено, активирует этот сервис-воркер и отправляет push-событие. Вам необходимо отслеживать это событие и отображать уведомление о его результате.

Добавьте следующий код в файл sw.js :

self.addEventListener('push', function(event) {
  console.log('[Service Worker] Push Received.');
  console.log(`[Service Worker] Push had this data: "${event.data.text()}"`);

  const title = 'Push Codelab';
  const options = {
    body: 'Yay it works.',
    icon: 'images/icon.png',
    badge: 'images/badge.png'
  };

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

Давайте разберём этот код пошагово. Вы отслеживаете события push в сервис-воркере, добавляя прослушиватель событий:

self.addEventListener('push', ... );

(Если вы раньше не работали с Web Workers, то self , вероятно, для вас новый объект. В файле Service Worker self ссылается на сам Service Worker.)

При получении push-сообщения будет вызван прослушиватель событий, и вы создадите уведомление, вызвав showNotification() в свойстве registration сервис-воркера. showNotification() требуется title ; вы также можете передать ему объект options , чтобы задать текст сообщения, значок и значок. (На момент написания статьи значок использовался только на Android.)

const title = 'Push Codelab';
const options = {
  body: 'Yay it works.',
  icon: 'images/icon.png',
  badge: 'images/badge.png'
};
self.registration.showNotification(title, options);

Последнее, что нужно учесть при обработке push -событий, — это event.waitUntil() . Этот метод принимает обещание, позволяющее браузеру поддерживать работу сервис-воркера до тех пор, пока переданное обещание не будет выполнено.

Чтобы сделать код выше немного более понятным, вы можете переписать его следующим образом:

const notificationPromise = self.registration.showNotification(title, options);
event.waitUntil(notificationPromise);

Теперь, когда вы разобрались с событием push, давайте протестируем событие push.

Попробуйте это

Благодаря обработке событий push в Service Worker вы можете инициировать фальшивое событие push, чтобы проверить, что произойдет при получении сообщения.

В вашем веб-приложении подпишитесь на push-уведомления и убедитесь, что в консоли отображается User IS subscribed . На панели «Приложение» в DevTools, на вкладке «Сервисные работники» , нажмите кнопку «Push» :

После нажатия кнопки Push вы увидите такое уведомление:

Примечание: Если этот шаг не помог, попробуйте отменить регистрацию вашего Service Worker с помощью ссылки «Отменить регистрацию» на панели приложений DevTools, дождитесь остановки Service Worker, а затем перезагрузите страницу.

Завершенный код

Если вы нажмёте на одно из этих уведомлений, вы заметите, что ничего не происходит. Вы можете обрабатывать нажатия уведомлений, прослушивая события notificationclick в вашем сервис-воркере.

Начните с добавления прослушивателя notificationclick в sw.js :

self.addEventListener('notificationclick', function(event) {
  console.log('[Service Worker] Notification click received.');

  event.notification.close();

  event.waitUntil(
    clients.openWindow('https://developers.google.com/web')
  );
});

Когда пользователь нажимает на уведомление, будет вызван прослушиватель событий notificationclick .

Сначала код закрывает уведомление, по которому был сделан щелчок:

event.notification.close();

Затем открывается новое окно или вкладка, загружающая URL-адрес https://developers.google.com/web . Вы можете изменить это.

event.waitUntil(
    clients.openWindow('https://developers.google.com/web/')
  );

event.waitUntil() гарантирует, что браузер не завершит работу Service Worker до отображения нового окна или вкладки.

Попробуйте это

Попробуйте снова запустить push-уведомление в DevTools и нажмите на уведомление. Теперь уведомление закроется, и откроется новая вкладка.

Вы убедились, что ваше веб-приложение может отображать уведомления с помощью DevTools, и узнали, как закрыть уведомление одним кликом. Следующий шаг — отправка push-уведомления.

Обычно для этого требуется отправить подписку с веб-страницы на бэкенд. Бэкенд затем инициирует push-уведомление, выполняя API-вызов к конечной точке в подписке.

Это выходит за рамки данной практической работы, но вы можете использовать сопутствующий сайт ( web-push-codelab.glitch.me ) для отправки push-уведомлений. Вставьте подписку в нижнюю часть страницы:

Затем вставьте это на сопутствующий сайт в текстовое поле «Подписка на рассылку» :

В поле Текст для отправки добавьте любую строку, которую вы хотите отправить вместе с push-сообщением.

Нажмите кнопку «Отправить push-сообщение» .

После этого вы получите push-уведомление. Использованный вами текст будет выведен в консоль.

Это даст вам возможность протестировать отправку и получение данных, а также манипулировать уведомлениями.

Сопутствующее приложение — это просто узел-сервер, использующий библиотеку web-push для отправки сообщений. Стоит изучить сайт web-push-libs.org на GitHub, чтобы узнать, какие библиотеки доступны для отправки push-сообщений. Оно обрабатывает множество деталей, необходимых для запуска push-сообщений.

Весь код сопутствующего сайта можно увидеть здесь .

Завершенный код

Единственное, чего не хватает, — это возможности отписать пользователя от push-уведомлений. Для этого нужно вызвать unsubscribe() для PushSubscription .

Вернитесь в файл scripts/main.js и измените прослушиватель нажатия кнопки pushButton в initializeUI() на следующее:

pushButton.addEventListener('click', function() {
  pushButton.disabled = true;
  if (isSubscribed) {
    unsubscribeUser();
  } else {
    subscribeUser();
  }
});

Обратите внимание, что теперь вы собираетесь вызвать новую функцию unsubscribeUser() . В этой функции вы получаете текущую подписку и вызываете для неё unsubscribe() . Добавьте следующий код в scripts/main.js :

function unsubscribeUser() {
  swRegistration.pushManager.getSubscription()
  .then(function(subscription) {
    if (subscription) {
      return subscription.unsubscribe();
    }
  })
  .catch(function(error) {
    console.log('Error unsubscribing', error);
  })
  .then(function() {
    updateSubscriptionOnServer(null);

    console.log('User is unsubscribed.');
    isSubscribed = false;

    updateBtn();
  });
}

Давайте рассмотрим эту функцию пошагово.

Сначала вы получаете текущую подписку, вызывая getSubscription() :

swRegistration.pushManager.getSubscription()

Возвращает обещание, которое разрешается с помощью PushSubscription , если таковая существует; в противном случае возвращается null . Если подписка есть, вы вызываете для неё unsubscribe() , что делает PushSubscription недействительной.

swRegistration.pushManager.getSubscription()
.then(function(subscription) {
  if (subscription) {
    // TODO: Tell application server to delete subscription
    return subscription.unsubscribe();
  }
})
.catch(function(error) {
  console.log('Error unsubscribing', error);
})

Вызов unsubscribe() возвращает обещание, поскольку его выполнение может занять некоторое время. Вы возвращаете это обещание, чтобы следующий then() в цепочке ожидал завершения unsubscribe() . Вы также добавляете обработчик catch на случай, если вызов unsubscribe() приведёт к ошибке. После этого вы можете обновить пользовательский интерфейс.

.then(function() {
  updateSubscriptionOnServer(null);

  console.log('User is unsubscribed.');
  isSubscribed = false;

  updateBtn();
})

Попробуйте это

Вы должны иметь возможность нажать «Включить push-сообщения» или «Отключить push-сообщения» в своем веб-приложении, и в журналах будет отображаться информация о том, что пользователь подписался или отписался.

Поздравляю с завершением этой лабораторной работы!

В этой лабораторной работе показано, как добавить push-уведомления в веб-приложение. Если вы хотите узнать больше о возможностях веб-уведомлений, ознакомьтесь с этими документами .

Если вы хотите внедрить push-уведомления на своём сайте, вам может быть интересно добавить поддержку старых браузеров или браузеров, не соответствующих стандартам, которые используют GCM. Подробнее здесь .

Дальнейшее чтение

Соответствующие записи в блоге