Add JS for subscribing to and receiving push notifications

This commit is contained in:
Karina Kwiatek 2022-09-11 20:42:40 +02:00
parent 8b98c278da
commit 2da4767623
9 changed files with 125 additions and 0 deletions

View file

@ -15,6 +15,7 @@ import initModeration from 'retrospring/features/moderation';
import initMemes from 'retrospring/features/memes'; import initMemes from 'retrospring/features/memes';
import initLocales from 'retrospring/features/locales'; import initLocales from 'retrospring/features/locales';
import initFront from 'retrospring/features/front'; import initFront from 'retrospring/features/front';
import initWebpush from 'retrospring/features/webpush';
start(); start();
document.addEventListener('DOMContentLoaded', initAnswerbox); document.addEventListener('DOMContentLoaded', initAnswerbox);
@ -28,6 +29,7 @@ document.addEventListener('DOMContentLoaded', initModeration);
document.addEventListener('DOMContentLoaded', initMemes); document.addEventListener('DOMContentLoaded', initMemes);
document.addEventListener('turbo:load', initLocales); document.addEventListener('turbo:load', initLocales);
document.addEventListener('turbo:load', initFront); document.addEventListener('turbo:load', initFront);
document.addEventListener('DOMContentLoaded', initWebpush);
window['Stimulus'] = Application.start(); window['Stimulus'] = Application.start();
const context = require.context('../retrospring/controllers', true, /\.ts$/); const context = require.context('../retrospring/controllers', true, /\.ts$/);

View file

@ -0,0 +1,7 @@
export function dismissHandler (event: Event): void {
event.preventDefault();
const sender: HTMLButtonElement = event.target as HTMLButtonElement;
sender.closest<HTMLDivElement>('.push-settings').classList.add('d-none');
localStorage.setItem('dismiss-push-settings-prompt', 'true');
}

View file

@ -0,0 +1,59 @@
import { get, post } from '@rails/request.js';
import I18n from "retrospring/i18n";
import { showNotification } from "utilities/notifications";
export function enableHandler (event: Event): void {
event.preventDefault();
try {
installServiceWorker()
.then(subscribe)
.then(async subscription => {
return Notification.requestPermission().then(permission => {
if (permission != "granted") {
return;
}
post('/ajax/web_push', {
body: {
subscription
},
contentType: 'application/json'
}).then(async response => {
const data = await response.json;
if (data.success) {
new Notification(I18n.translate("frontend.push_notifications.subscribe.success.title"), {
body: I18n.translate("frontend.push_notifications.subscribe.success.body")
});
} else {
new Notification(I18n.translate("frontend.push_notifications.fail.title"), {
body: I18n.translate("frontend.push_notifications.fail.body")
});
}
});
});
});
} catch (error) {
console.error("Failed to set up push notifications", error);
showNotification(I18n.translate("frontend.push_notifications.setup_fail"));
}
}
async function installServiceWorker(): Promise<ServiceWorkerRegistration> {
return navigator.serviceWorker.register("/service_worker.js", { scope: "/" });
}
async function getServerKey(): Promise<Buffer> {
const response = await get("/ajax/webpush/key");
const data = await response.json;
return Buffer.from(data.key, 'base64');
}
async function subscribe(registration: ServiceWorkerRegistration): Promise<PushSubscription> {
const key = await getServerKey();
return await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: key
});
}

View file

@ -0,0 +1,28 @@
import registerEvents from 'retrospring/utilities/registerEvents';
import { enableHandler } from './enable';
import { dismissHandler } from "./dismiss";
export default (): void => {
const swCapable = 'serviceWorker' in navigator;
if (swCapable) {
document.body.classList.add('cap-service-worker');
}
const notificationCapable = 'Notification' in window;
if (notificationCapable) {
document.body.classList.add('cap-notification');
}
if (swCapable && notificationCapable) {
navigator.serviceWorker.getRegistration().then(registration => {
if (!registration && localStorage.getItem('dismiss-push-settings-prompt') == null) {
document.querySelector('.push-settings').classList.remove('d-none');
}
})
registerEvents([
{type: 'click', target: '[data-action="push-enable"]', handler: enableHandler, global: true},
{type: 'click', target: '[data-action="push-dismiss"]', handler: dismissHandler, global: true},
]);
}
}

View file

@ -0,0 +1,5 @@
.card.push-settings.d-none
.card-body
= t(".description")
%button.btn.btn-primary{ data: { action: "push-enable" } }= t("voc.y")
%button.btn{ data: { action: "push-dismiss" } }= t("voc.n")

View file

@ -2,6 +2,7 @@
.row .row
.col-sm-10.col-md-10.col-lg-9.mx-auto .col-sm-10.col-md-10.col-lg-9.mx-auto
= render 'inbox/actions', delete_id: @delete_id, disabled: @disabled, inbox_count: @inbox_count = render 'inbox/actions', delete_id: @delete_id, disabled: @disabled, inbox_count: @inbox_count
= render 'inbox/push_settings'
= render 'layouts/messages' = render 'layouts/messages'
= yield = yield

View file

@ -49,6 +49,15 @@ en:
confirm: confirm:
title: "Are you sure?" title: "Are you sure?"
text: "This will mute this user for everyone." text: "This will mute this user for everyone."
push_notifications:
subscribe:
success:
title: Push notifications enabled!
body: You will now receive push notifications for new questions on this device.
fail:
title: Failed to subscribe to push notifications
body: Please try again later
setup_fail: Failed to set up push notifications. Please try again later.
report: report:
confirm: confirm:
title: "Are you sure you want to report this %{type}?" title: "Are you sure you want to report this %{type}?"

View file

@ -231,6 +231,8 @@ en:
share: share:
heading: "Share" heading: "Share"
button: "Share on %{service}" button: "Share on %{service}"
push_settings:
description: "Want to receive push notifications for new questions on your device?"
layouts: layouts:
feedback: feedback:
heading: "Feedback" heading: "Feedback"

12
public/service_worker.js Normal file
View file

@ -0,0 +1,12 @@
self.addEventListener('push', function (event) {
if (event.data) {
const notification = event.data.json();
console.log(event.data);
event.waitUntil(self.registration.showNotification(notification.title, {
body: notification.body
}));
} else {
console.error("Push event received, but it didn't contain any data.", event);
}
});