Показываем динамические сообщения используя Web Notification API

Показываем динамические сообщения используя Web Notification API

Мы живем в мире где уведомления от ваших любимых сайтов или приложений больше не существуют только в границах вашего смартфона. Теперь привычно получать уведомления прямо из вашего браузера. Например, Facebook отправляет вам уведомления если у вас есть новая заявка в друзья ил кто-то комментирует историю, где вас упомянули. Slack, популярное приложение для обмена сообщениями, отправляют вам уведомления когда вас упомянули в общении.

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

В этой статье мы реализуем систему уведомлений на сайте Concise CSS которая будет предупреждать пользователей о выходе новой версии фрэймворка. Собираюсь показать вам, как я этого достиг используя localStorage и Notification API браузера.

Уведомление браузера отображается на сайте concisecss.com, используя API

Основы Notification API

Для начала, мы должны определить поддерживает браузер пользователя уведомления или нет. Основная работа на протяжении всего урока будет сделана с помощью объекта уведомлений.

(function() {
  if ("Notification" in window) {

  }
})();

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

Мы сохраняем вывод свойства permission в переменную. Если разрешение предоставлено или отклонено то мы ничего не получим. Если мы еще не запрашивали разрешение, то сделаем это методом requestPermission.

(function() {
  if ("Notification" in window) {
    var permission = Notification.permission;
    if (permission === "denied" || permission === "granted") {
      return;
    }
    Notification.requestPermission();
  }
})();

Popup letting the user to allow or block browser notifications

Всплывающее окно спрашивает разрешение

Вы должны увидеть в вашем браузере похожее уведомление как на на картинке выше.

Теперь когда мы спросили разрешение, давайте изменим наш код, чтобы показать уведомление если разрешение получено:

(function() {
  if ("Notification" in window) {
    var permission = Notification.permission;

    if (permission === "denied" || permission === "granted") {
      return;
    }

    Notification
      .requestPermission()
      .then(function() {
        var notification = new Notification("Hello, world!");
      });
  }
})();
Всплывающее окно с текстом Hello World

Скучно, но функционально.

Здесь мы использовали основанный на обещаниях синтаксис метода requestPermission() для показа уведомлений после того как получили разрешение. Мы показываем уведомление используя конструктор. Конструктор принимает два параметра, первый для заголовка уведомления и второй для опций. Перейдите по ссылке на документацию, чтобы найти полный список опций, которые могут быть переданы.

Сохранение версий фреймворка

Ранее в этой статье, я упомянул, что мы будем использовать LocalStorage, чтобы помочь показывать уведомления. Использование LocalStorage является предпочтительным способом хранения информации на стороне клиента в JavaScript. Мы создадим в LocalStorage ключ с именем conciseVersion который будет хранить текущую версию фрейворка (т.е. 1.0.0). Затем мы можем использовать этот ключ, чтобы проверить наличие новых версий фреймворка.

Как мы будем обновлять значение conciseVersion? Нам нужен способ устанавливать текущую версию всякий раз, когда кто-то посещает сайт. Также нам нужно обновлять это значение, всякий раз, когда выпускается новая версия. Каждый раз когда меняется значение conciseVersion, мы должны показать посетителю уведомление объявляющее новую версию фреймворка. Мы решим эту проблему добавив скрытый элемент на страницу. Этот элемент будет иметь класс .js-currentVersion и будет содержать только текущую версию фреймворка. Так как элемент находится в DOM, мы можем довольно просто взаимодействовать с ним используя JavaScript.

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

<span class="js-currentVersion" aria-hidden="true">3.4.0</span>

Используем немного CSS, чтобы спрятать элемент:

[aria-hidden="true"] {
  display: none;
  visibility: hidden;
}

Примечание: так как элемент не содержит никакого значимого контента, скринридерам не нужно получать доступ к ниму. Вот почему я указал атрибуту aria-hidden значение true и использовал display: none, чтобы его скрыть. Пожалуйста, обратитесь к этой статье WebAIM для большой статьи на спрятанном контенте.

Теперь мы можем взять этот элемент и взаимодействовать с ним в JavaScript. Нам нужно написать функцию, которая возвращает текст скрытого элемента, который мы только что создали.

function checkVersion() {
  var latestVersion = document.querySelector(".js-currentVersion").textContent;
}

Эта функция сохраняет контент элемента .js-currentVersion используя свойство textContent. Давайте добавим переменную, которая будет хранить содержимое нашего conciseVersion из localStorage.

function checkVersion() {
  var latestVersion = document.querySelector(".js-currentVersion").textContent;
  var currentVersion = localStorage.getItem("conciseVersion");
}

Теперь у нас есть переменная с последней версией фреймворка, и мы храним наш LocalStorage ключ к переменной. Пришло время, чтобы добавить логику, которая определит, доступна ли новая версия фреймворка или нет.

первым делом мы проверим существует ли значение в ключе conciseVersion. Если нет, мы покажем пользователю уведомление, так как, скорее всего, это его первый визит. Если значение существует, мы проверим, что это значение (сохраненное в переменной currentVersion) больше чем текущая версия (переменная latestVersion). Если последняя версия фрейворка больше, чем версия которую пользователь видел в последний раз, мы узнаем, что вышла новая версия.

Примечание: для сравнения двух версий строк мы будем использовать библиотеку semver-compare.

Зная это мы показываем посетителю уведомление и обновляем наше значение в ключе conciseVersion.

function checkVersion() {
  var latestVersion = document.querySelector(".js-currentVersion").textContent;
  var currentVersion = localStorage.getItem("conciseVersion");

  if (currentVersion === null || semverCompare(currentVersion, latestVersion) === -1) {      
    var notification = new Notification("Hello, world!");

    localStorage.setItem("conciseVersion", latestVersion);
  }
}

Чтобы использовать эту функцию, нам нужно изменить наш код разрешений:

(function() {
  if ("Notification" in window) {
    var permission = Notification.permission;

    if (permission === "denied") {
      return;
    } else if (permission === "granted") {
      return checkVersion();
    }

    Notification.requestPermission().then(function() {
      checkVersion();
    });
  }
})();

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

Показываем уведомления

До сих пор мы показывали пользователям только простые уведомления, которые не содержали много информации. Напишем функцию, которая позволит на лету создать уведомления и контролировать множество других аспектов наших уведомлений.

Функция имеет параметры для основного текста, иконки заголовка а также дополнительную ссылка и продолжительность уведомления. Внутри, мы создаем объект, который хранит опции основной текст уведомления и значок. Также создадим новый экземпляр объекта Notification, получающий в качестве параметров, заголовок уведомления и объект опций.

Следом, мы добавим обработчик события onclick если мы хотим связать ссылку к нашим уведомлениям. Мы используем setTimeout(), чтобы закрыть уведомление через указанное время. Если время не указывается при вызове функции, по умолчанию используется пять секунд.

function displayNotification(body, icon, title, link, duration) {
  link = link || null; 
  duration = duration || 5000; 

  var options = {
    body: body,
    icon: icon
  };

  var n = new Notification(title, options);

  if (link) {
    n.onclick = function () {
      window.open(link);
    };
  }

  setTimeout(n.close.bind(n), duration);
}

Теперь, изменим checkVersion(), чтобы показывать посетителю больше информации.

function checkVersion() {
  var latestVersion = document.querySelector(".js-currentVersion").textContent;
  var currentVersion = localStorage.getItem("conciseVersion");

  if (currentVersion === null || semverCompare(currentVersion, latestVersion) === -1) {      
    displayNotification(
      `Click to see what's new in v${latestVersion}`,
      "http://concisecss.com/images/logo.png",
      "A new version of Concise is available",
      `https://github.com/ConciseCSS/concise.css/releases/v${latestVersion}`
    );

    localStorage.setItem("conciseVersion", latestVersion);
  }
}

Мы используем нашу функцию displayNotification, чтобы добавить описание, картинку, заголовок и ссылку к нашему уведомлению.

Примечание: мы использовали ES6 template literals чтобы вставить выражения в наш текст.

Полный код и тестирование

Ниже вы можете увидеть полный код который мы написали на этом уроке.

(function() {
 // Check permissions
 if ("Notification" in window) {
   var permission = Notification.permission;  

   if (permission === "denied") {
     return;
   } else if (permission === "granted") {
     return checkVersion();
   }  

   Notification.requestPermission().then(function() {
     checkVersion();
   });
 }

 function checkVersion() {
   // Retrieve current version
   var latestVersion = document.querySelector(".js-currentVersion").textContent;
   var currentVersion = localStorage.getItem("conciseVersion");
   if (currentVersion === null || semverCompare(currentVersion, latestVersion) === -1 ) {
     displayNotification(
       `Click to see what's new in v${latestVersion}`,
       "https://s3-us-west-2.amazonaws.com/s.cdpn.io/123941/concise-logo.png",
       "A new version of Concise is available",
       `https://github.com/ConciseCSS/concise.css/releases/v${latestVersion}`
     );  

     localStorage.setItem("conciseVersion", latestVersion);
   }
 }

 function displayNotification(body, icon, title, link, duration) {
   link = link || 0;
   duration = duration || 5000;
   var options = {
     body: body,
     icon: icon
   };

   var n = new Notification(title, options);

   if (link) {
     n.onclick = function () {
       window.open(link);
     };
   }
   setTimeout(n.close.bind(n), duration);
 }
}());

Запуск этого кода должен создать следующее уведомление в вашем браузере.

Финальный пример уведомления

Для тестирования, вам нужно знать разрешения вашего браузера. Здесь некоторые ссылки для управления уведомлениями в Google Chrome, Safari, FireFox, и Microsoft Edge. Кроме того, для удобства тестирования, вам должны быть знакома консоль разработчика, чтобы удалять и изменять значения в LocalStorage.

Вы можете тестировать пример, запустив скрипт один раз и изменить значение .js-currentVersion HTML элемента так, чтобы скрипт увидел различие. Вы также можете запускать, используя ту же версию, чтобы проверить, что вы не получаете повторных уведомлений.

Это все, что нам нужно для динамических уведомлений! Если вы ищете большей гибкости для оповещений браузера, я рекомендую изучить Service Worker API. Service workers могут использоваться, чтобы реагировать на push notifications и позволяет пользователям получать уведомления, независимо от того, находятся они сейчас на сайте или нет, в результате чего получаются более своевременные уведомления.