Как настроить тело и значок веб-уведомления браузера?

Как мы можем динамически настроить уведомление браузера icon и body на основе @challenge.image и @challenge.description?

введите здесь описание изображения

Я получил всплывающее уведомление, нажав webpush-button, но динамически меняется только title сообщения. Как я могу заставить его работать и для body, и для icon?

вызовы/show.html.erb

<script>
  $('.webpush-button').on('click', (e) => {
    navigator.serviceWorker.ready
    .then((serviceWorkerRegistration) => {
      serviceWorkerRegistration.pushManager.getSubscription()
      .then((subscription) => {
        $.post('/push', {
          subscription: subscription.toJSON(),
          message: "<%= @challenge.name %>", # This works dynamically for the title, but how can I get message to also dynamically send body and icon so I can use <%= @challenge.image %> for icon and <%= @challenge.description %> for body?
        });
      });
    });
  });
</script>

push_notifications_controller

class PushNotificationsController < ApplicationController
  def push
    Webpush.payload_send(
      message: params[:message],
      endpoint: params[:subscription][:endpoint],
      p256dh: params[:subscription][:keys][:p256dh],
      auth: params[:subscription][:keys][:auth],
      vapid: {
        subject: "mailto:[email protected]",
        public_key: ENV['VAPID_PUBLIC_KEY'],
        private_key: ENV['VAPID_PRIVATE_KEY']
      }
    )
  end
end

application.js

  if (navigator.serviceWorker) {
    navigator.serviceWorker.register('/serviceworker.js')
    .then(function(reg) {
       console.log('Service worker change, registered the service worker');
    });
  }
  // Otherwise, no push notifications :(
  else {
    console.error('Service worker is not supported in this browser');
  }

  // When serviceWorker is supported, installed, and activated,
  // subscribe the pushManager property with the vapidPublicKey
  navigator.serviceWorker.ready.then((serviceWorkerRegistration) => {
    serviceWorkerRegistration.pushManager.subscribe({
      userVisibleOnly: true,
      applicationServerKey: window.vapidPublicKey
    });
  });

  // Let's check if the browser supports notifications
  if (!("Notification" in window)) {
    console.error("This browser does not support desktop notification");
  }

  // Let's check whether notification permissions have already been granted
  else if (Notification.permission === "granted") {
    console.log("Permission to receive notifications has been granted");
  }

  // Otherwise, we need to ask the user for permission
  else if (Notification.permission !== 'denied') {
    Notification.requestPermission(function (permission) {
    // If the user accepts, let's create a notification
      if (permission === "granted") {
        console.log("Permission to receive notifications has been granted");
      }
    });
  }

serviceworker.js.erb

console.log('[Service Worker] Hello world!');

var CACHE_VERSION = 'v1';
var CACHE_NAME = CACHE_VERSION + ':sw-cache-';

function onInstall(event) {
  console.log('[Serviceworker]', "Installing!", event);
  event.waitUntil(
    caches.open(CACHE_NAME).then(function prefill(cache) {
      return cache.addAll([

        // make sure serviceworker.js is not required by application.js
        // if you want to reference application.js from here
        '<%#= asset_path "application.js" %>',

        '<%= asset_path "application.css" %>',

        '/offline.html',

      ]);
    })
  );
}

function onActivate(event) {
  console.log('[Serviceworker]', "Activating!", event);
  event.waitUntil(
    caches.keys().then(function(cacheNames) {
      return Promise.all(
        cacheNames.filter(function(cacheName) {
          // Return true if you want to remove this cache,
          // but remember that caches are shared across
          // the whole origin
          return cacheName.indexOf(CACHE_VERSION) !== 0;
        }).map(function(cacheName) {
          return caches.delete(cacheName);
        })
      );
    })
  );
}

// Borrowed from https://github.com/TalAter/UpUp
function onFetch(event) {
  event.respondWith(
    // try to return untouched request from network first
    fetch(event.request).catch(function() {
      // if it fails, try to return request from the cache
      return caches.match(event.request).then(function(response) {
        if (response) {
          return response;
        }
        // if not found in cache, return default offline content for navigate requests
        if (event.request.mode === 'navigate' ||
          (event.request.method === 'GET' && event.request.headers.get('accept').includes('text/html'))) {
          console.log('[Serviceworker]', "Fetching offline content", event);
          return caches.match('/offline.html');
        }
      })
    })
  );
}

self.addEventListener("push", (event) => {
  let title = (event.data && event.data.text()) || "Yay a message";
  let body = "We have received a push message";
  let icon = '/assets/default.png';

  event.waitUntil(
    self.registration.showNotification(title, { title,body, icon })
  )
});

self.addEventListener('install', onInstall);
self.addEventListener('activate', onActivate);
self.addEventListener('fetch', onFetch);

serviceworker-companion.js

if (navigator.serviceWorker) {
  navigator.serviceWorker.register('/serviceworker.js', { scope: './' })
    .then(function(reg) {
      console.log('[Companion]', 'Service worker registered!');
    });
}

Я реализовал push через гем serviceworker, жемчужина webpush, Учебник VAPID.


person AnthonyGalli.com    schedule 26.04.2017    source источник


Ответы (2)


Объект event.data в self.addEventListener("push", (event)=> {...}) имеет метод json(), который преобразует в json значение event.data.

Это будет работать, только если отправляемые данные имеют правильный формат json.

Вот пример того, как я использую веб-пуш:

self.addEventListener("push", function onPush(event) {
  var data = event.data.json()
  event.waitUntil(self.registration.showNotification(data.message.title, {
    body: data.message.body,
    icon: data.message.icon,
    actions: [ 
      { action: 'Button one', title: "Button one text" },
      { action: 'Button two', title: "Button two text" }
    ]
  }));
});

Вот метод из моей модели user.rb:

 def send_web_push(title: , body: )
    #  the web_push is a payload with I save to the user record,
    #  that's allow me to send web push with background worker. 
    payload = {
      endpoint: web_push['endpoint'], 
      keys: {
        auth: web_push['auth'],
        p256dh: web_push['p256dh'] 
      }
    }
    push = WebPush.new(payload)
    push.set_vapid_details(
      'mailto:[email protected]',
      Rails.application.secrets.web_push_public_key,
      Rails.application.secrets.web_push_private_key
    )
    # below is a `:message` key in json format
    push.send_notification({
      message: {
        title: title,                 
        body: body,                  
        icon: "/this_is_my_icon.png" 
      }
    }.to_json)
  end

Я использую какой-то другой рубиновый камень для web_push, но я полагаю, что это обычное дело. Из этого примера вы должны увидеть, как могут быть изменены ключи body, title и icon.

person Зелёный    schedule 26.04.2017
comment
Вот так user.send_web_push(title: 'Here is a title', body: 'Here is a body') - person Зелёный; 28.04.2017
comment
Я показал только пример, это значит, что ваш код может быть другим. Это пример фактического рабочего кода в моем проекте. Я не ваш инструмент отладки или среда разработки. Вот драгоценный камень, который я использую github.com/miyucy/web_push - person Зелёный; 28.04.2017
comment
Лучшее спасибо, что вы можете сделать, это одобрить или проголосовать за ответ. Не надо слов, сделай дело. - person Зелёный; 28.04.2017

просмотреть

<%= content_tag(:button, "Foo", class: "webpush-button") %>

<script>
  $('.webpush-button').on('click', (e) => {
    navigator.serviceWorker.ready
    .then((serviceWorkerRegistration) => {
      serviceWorkerRegistration.pushManager.getSubscription()
      .then((subscription) => {
        $.post('/push', {
          subscription: subscription.toJSON(),
          message: "<%= @challenge.name %>", 
          body: "<%= @challenge.why %>",
          icon: "/assets/default.png"
        });
      });
    });
  });
</script>

контроллер

class PushNotificationsController < ApplicationController
  def push
    Webpush.payload_send(
      message: JSON.generate({ 
        title: params[:message],
        body: params[:body],
        icon: params[:icon]
        }),
      endpoint: params[:subscription][:endpoint],
      p256dh: params[:subscription][:keys][:p256dh],
      auth: params[:subscription][:keys][:auth],
      vapid: {
        subject: "mailto:[email protected]",
        public_key: ENV['VAPID_PUBLIC_KEY'],
        private_key: ENV['VAPID_PRIVATE_KEY']
      }
    )
  end
end

JS

self.addEventListener("push", (event) => {
  var data = event.data.json();
  let title = (event.data && data.title) || "Yay a message";
  let body =  (event.data && data.body) || "We have received a push message";
  let icon =  (event.data && data.icon) || "/assets/blue-astronaut.png";

  event.waitUntil(
    self.registration.showNotification(title, { title,body, icon })
  )
});
person AnthonyGalli.com    schedule 01.05.2017