ForgeViewer и прогрессивные web-приложения
Цитируя (англ., по-русски статья немного отличается и внимание акцентируется на несколько иные вещи):
Прогрессивное web-приложение (PWA) - тип программного приложения, доставляемого по сети и использующего общие web технологии, включая HTML, CSS и JavaScript. Они предназначены для работы на любых платформах с использованием браузеров, следующих web-стандартам, включая как десктопные, так и мобильные устройства.
Эти приложения обычно используют современные Web API, такие как Service Workers, Push Notifications и IndexedDB. Мы уже писали о том, как Service Worker API может помочь нам реализовать сценарий работы с приложением в оффлайн (перевод статьи есть на нашем сайте) и сегодня мы посмотрим на другие API и посмотрим, что нужно сделать, чтобы наше приложение Forge стало настоящим прогрессивным web приложением. В качестве основы мы возьмём https://github.com/petrbroz/forge-basic-app и превратим его в приложение, которое Вы можете установить на своё iOS или Android устройство, запустив именно как приложение (т.е. Вы не увидите строки адреса или чего-то напоминающего Chrome mobile), которое даже будет уведомлять Вас о том, что преобразование модели сервисом Model Derivative завершено!
Manifest
Прогрессивное web-приложение обязано иметь Web Application Manifest. Это то место, где мы описываем, как должно выглядеть и вести себя приложение после того, как оно будет установлено пользователем, например:
- иконка и подпись
- цвета фона и темы (используются, например, на экране заставки)
- стартовый URL и display mode
В нашем приложении мы добавим новый файл app.webmanifest в папку public (где находятся все статические файлы, используемые приложением). Его содержание:
- {
- "name": "Forge Basic App",
- "short_name": "ForgeApp",
- "description": "Sample application showing the basic usage of Autodesk Forge.",
- "icons": [
- {
- "src": "/logo.png",
- "type": "image/png",
- "sizes": "48x48 72x72 96x96 128x128 256x256"
- }
- ],
- "start_url": "/",
- "display": "fullscreen",
- "theme_color": "#ffa835",
- "background_color": "#ffffff"
- }
В файле public/index.html в теге <head> добавим ссылку на созданный manifets:
Даже с этим простым обновлением нам уже доступны некоторые преимущества PWA. Если Вы используете HTTPS (поскольку PWA могут быть запущены только в защищенном контексте), и запускаете главную страницу приложения с Вашего смартфона, система запросит установку приложения на Ваше устройство. Установленное приложение будет использовать иконку и подпись, которую мы задали в файле manifest-а, если Вы запустите его, оно будет в "полноэкранном" режиме, т.е. весь UI браузера будет скрыт. Здорово, правда?
Push Notifications
Push API может быть использован для отправки уведомлений Вашему приложению даже в том случае, если оно не открыто в текущий момент.
Примечание: Ваше приложение может использовать уведомления, даже если оно не PWA. Это API также может быть использовано в обычных web-приложениях в браузере.
Отправка push-уведомлений на стороне сервера - это не совсем простая задача, но, к счастью, есть различные поставщики подобных услуг, которые могут помочь с их реализацией, например, Amazon Simple Notification Service или Pusher Beams.
В нашем примере, тем не менее, мы не будем использовать сторонние поставщики услуг, вместо этого воспользуемся NPM модулем -web-push. Этот модуль предоставляет скрипт командной строки, с помощью которого мы сможем создать так называемые VAPID ключи следующим образом:
- web-push generate-vapid-keys
Серверная часть приложения
Чтобы включить push-уведомления в нашем серверном приложении, нам потребуется сделать следующее:
1. Зарегистрироать ключи VAPID в модуле web-push:
- //...
- const webpush = require('web-push');
- const { VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY } = process.env;
- webpush.setVapidDetails('mailto:your.email@domain.com', VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY);
- //...
2. Добавить новый метод API, по которому клиентская часть приложения может подписаться на уведомления (в нашем случае сохраняем все подписки в свойстве приложения "subs"):
- //...
- router.post('/push/subscribe', express.json(), async (req, res) => {
- const subscription = req.body;
- const subscriptions = req.app.get('subs') || [];
- subscriptions.push(subscription);
- req.app.set('subs', subscriptions);
- res.status(201).end();
- });
- //...
3. Рассылаем уведомления при необходимости:
- //...
- const subscriptions = req.app.get('subs');
- if (subscriptions) {
- const payload = JSON.stringify({ name: 'something', foo: 'bar' });
- for (const subscription of subscriptions) {
- webpush.sendNotification(subscription, payload)
- .catch(error => console.error(error.stack));
- }
- }
- //...
Клиентская часть приложения
Теперь мы можем обновить наше клиентское приложение так, чтобы оно могло подписаться на уведомления (с согласия пользователя, конечно!), а так же обработать уведомления, отправленные сервером.
Подписка обрабатывается с помощью объекта push manager, который становится доступным после регистрации service worker-а для нашего web-приложения. После вызова метода pushManager.subscribe, в который мы передадим ключ VAPID (преобразованный в Uint8Array из BASE64), браузер запросит у пользователя согласие на получение уведомления. Если пользователь согласился, мы получим объект, который мы можем отправить в метод /push/subscribe:
Примечание: ключ VAPID мы передаем, используя cookie
- //...
- if ('serviceWorker' in navigator) {
- const registration = await navigator.serviceWorker.register('/service-worker.js', { scope: '/' });
- setupNotifications(registration.pushManager);
- }
- //...
- async function setupNotifications(pushManager) {
- function urlBase64ToUint8Array(base64String) {
- const padding = '='.repeat((4 - base64String.length % 4) % 4);
- const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');
- const rawData = atob(base64);
- const outputArray = new Uint8Array(rawData.length);
- for (let i = 0; i < rawData.length; ++i) {
- outputArray[i] = rawData.charCodeAt(i);
- }
- return outputArray;
- }
- const match = document.cookie.match(/VAPID_PUBLIC_KEY=([\w\d_\+\-\/]+)/);
- if (match) {
- const vapidPublicKey = match[1];
- try {
- const subscription = await pushManager.subscribe({
- userVisibleOnly: true,
- applicationServerKey: urlBase64ToUint8Array(vapidPublicKey)
- });
- await fetch('/push/subscribe', {
- method: 'POST',
- body: JSON.stringify(subscription),
- headers: { 'Content-Type': 'application/json' }
- });
- } catch (err) {
- console.error(err);
- }
- }
- }
- //...
И наконец, мы предлагаем супер-простую реализацию нашего service worker-а, которое будет просто реагировать на любые уведомления со стороны сервера, показывая уведомление как системное:
- self.addEventListener('push', ev => {
- const data = ev.data.json();
- self.registration.showNotification('Translation Complete', {
- body: `Model: ${data.name}, status: ${data.status}`,
- icon: '/logo.png'
- });
- });
Вот и всё! Ваше PWA готово принимать уведомления от нашего сервера.
Если Вы хотите посмотреть, как исходное приложение https://github.com/petrbroz/forge-basic-app было преобразовано в PWA, посмотрите ветку experiment/pwa. И если Вы хотите развернуть и запустить приложение самостоятельно, в файле README Вы найдете больше информации о том, как сгенерировать ключи VAPID и передать их в приложение.
Источник: https://forge.autodesk.com/blog/forge-progressive-web-apps
Опубликовано 31.01.2021