noticed
noticed copied to clipboard
WebPush Delivery Method
Web Push requires a service worker and a model to work.
https://blog.mozilla.org/services/2016/08/23/sending-vapid-identified-webpush-notifications-via-mozillas-push-service/
The Webpush gem provides some helpful features for generating VAPID keys, etc.
Some sample code:
class DeliveryMethods::Webpush < Noticed::DeliveryMethods::Base
def deliver
recipient.webpush_subscriptions.each do |sub|
sub.publish(params[:message])
end
end
end
# == Schema Information
#
# Table name: webpush_subscriptions
#
# id :bigint not null, primary key
# endpoint :string
# auth_key :string
# p256dh_key :string
# created_at :datetime not null
# updated_at :datetime not null
# user_id :bigint not null
#
class WebpushSubscription < ApplicationRecord
belongs_to :user
def publish(message)
Webpush.payload_send(
message: message,
endpoint: endpoint,
p256dh: p256dh_key,
auth: auth_key,
vapid: {
private_key: Rails.application.credentials.dig(:webpush, :private_key),
public_key: Rails.application.credentials.dig(:webpush, :public_key)
}
)
end
end
class WebpushSubscriptionsController < ApplicationController
skip_before_action :verify_authenticity_token
def index
end
def create
notification = WebpushNotification.find_by(auth_key: params[:keys][:auth])
if !notification
notification = WebpushNotification.new(
user: current_user,
endpoint: params[:endpoint],
auth_key: params[:keys][:auth],
p256dh_key: params[:keys][:p256dh],
)
end
if notification.save
render json: notification
else
render json: notification.errors.full_messages
end
end
end
const vapidPublicKey = new Uint8Array(<%= Base64.urlsafe_decode64(Rails.application.credentials.dig(:webpush, :public_key)).bytes %>);
// application.js
// Register the serviceWorker script at /serviceworker.js from your server if supported
if (navigator.serviceWorker) {
navigator.serviceWorker.register('/service_worker.js')
.then(function(reg) {
navigator.serviceWorker.ready.then((serviceWorkerRegistration) => {
serviceWorkerRegistration.pushManager
.subscribe({
userVisibleOnly: true,
applicationServerKey: vapidPublicKey
}).then(async function(sub) {
const data = await fetch('/webpush_subscriptions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(sub),
}).then(r => r.json());
console.log(data);
});
});
});
} else {
console.error('Service worker is not supported in this browser');
}
@excid3 push endpoints expire after a while.
Webpush gem method has a method to check for expired endpoints. I use that to clean up the database
Not sure if it can be part of this gem but might be worth considering
something like this
rescue Webpush::Unauthorized # skip for now rescue Webpush::ExpiredSubscription sub.destroy end
which can rescue publish method
Good to know about the expirations. 👍 I haven't tried this out before, so lots to learn.
@excid3 service worker has an event called 'pushsubscriptionchange' when the push endpoint expires, the event gets triggered which can be used to create another push endpoint in the background.
something like this
// Update push subscription if any change is detected
function onPushSubcriptionChange(event) {
event.waitUntil(swRegistration.pushManager.subscribe(event.oldSubscription.options)
.then(subscription => {
const data = {
subscription: subscription,
};
return fetch("/notifications/create", {
method: "post",
headers: {
"Content-type": "application/json"
},
body: JSON.stringify(data),
});
})
);
}
self.addEventListener('pushsubscriptionchange', onPushSubcriptionChange);
I am curious how you will choose to handle that. I could not see a way to update an existing subscription record so using those rescue methods to clean up the database and create a new subscription. There is not an identifier to use I think
there is also this idea to include service worker as part of asset pipeline or webpacker
Not sure if this is still being considered for Noticed but big news today for WebPush. I've never bothered looking into WebPush until iOS/iPadOS devices supported it but it seems like WebPush will be becoming much more popular now
I created a video walking through setting this up with full support for iOS clients even. Was wondering if there would be interest in getting this pulled in as a PR. My big question would be around what the javascript resources would look like since so much of that is going to be custom.
Video Link: https://youtu.be/WHZ4strj6_U Backing repo: https://github.com/jbennett/web_push_notifications
I added a PR to automated most of the WebPush setup: #297
Thanks @jbennett can't wait to check this out!