How to use javascript click to trigger POST - javascript

When a user clicks...
<%= content_tag(:button, "Send", class: "webpush-button") %>
# Previous Attempt: <%= button_to 'send', class: "webpush-button" %>
<script>
$('.webpush-button').on('click', (e) => {
navigator.serviceWorker.ready
.then((serviceWorkerRegistration) => {
serviceWorkerRegistration.pushManager.getSubscription()
.then((subscription) => {
$.post("/post", {
subscription: subscription.toJSON(),
message: 'You clicked a button!'
});
});
});
});
</script>
he should be taken through...
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:sender#example.com",
public_key: ENV['VAPID_PUBLIC_KEY'],
private_key: ENV['VAPID_PRIVATE_KEY']
}
)
end
end
but instead nothing happens. The .webpush-button javascript never kicks in. I put it in two places and it still has no effect...
application.js
/ Register the serviceWorker script at /serviceworker.js from our server if supported
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
});
});
$('.webpush-button').on('click', (e) => {
navigator.serviceWorker.ready
.then((serviceWorkerRegistration) => {
serviceWorkerRegistration.pushManager.getSubscription()
.then((subscription) => {
$.post('/push', {
subscription: subscription.toJSON(),
message: 'You clicked a button!'
});
});
});
});
// 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");
}
});
}
application.html.erb
<script>
window.vapidPublicKey = new Uint8Array("<%= #decodedVapidPublicKey %>");
</script>
Now based on the tutorial and git code I used... the subscription should be gathered from the serviceworker so then why am I still getting a nil error?
I'm using serviceworker & webpush gems and followed this VAPID tutorial.
Not a duplicate. The other question is focused on params. This one is focused on javascript not triggering.

First, you should only put the javascript at one place.
Second, add the .webpush-button click method after page be ready
$(document).on('ready page:load', function () {
});
And in the chrome dev tools, Tab Event Listeners, check if the html element has click event.

Related

How to hard refresh Firefox after Service Worker update?

I have implemented the code below to clear service worker cache and reloads - after the user has accepted update of the service worker. The code works well in Chrome and Edge, but Firefox will not reload the page. Firefox will keep asking to install the same version until I hard refresh (shift reload) the page.
service-worker-base.js
// Imports
const CACHE_DYNAMIC_NAME = 'DEBUG-035'
setCacheNameDetails({ prefix: 'myApp', suffix: CACHE_DYNAMIC_NAME });
// Cache then network for css
registerRoute(
'/dist/main.css',
new StaleWhileRevalidate({
cacheName: `${CACHE_DYNAMIC_NAME}-css`,
plugins: [
new ExpirationPlugin({
maxEntries: 10, // Only cache 10 requests.
maxAgeSeconds: 60 * 60 * 24 * 7 // Only cache requests for 7 days
})
]
})
)
// Cache then network for images
//...
// Use a stale-while-revalidate strategy for all other requests.
setDefaultHandler(new StaleWhileRevalidate())
precacheAndRoute(self.__WB_MANIFEST)
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting()
}
})
// Clear cache before installing new service worker
self.addEventListener('activate', (event) => {
var cachesToKeep = ['none'];
event.waitUntil(
caches.keys().then((keyList) => {
return Promise.all(keyList.map((key) => {
if (cachesToKeep.indexOf(key) === -1) {
console.log('Delete cache', key)
return caches.delete(key);
}
}));
})
);
event.waitUntil(self.clients.claim());
});
//...
app.js
const enableServiceWorker = process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'qa'
const serviceWorkerAvailable = ('serviceWorker' in navigator) ? true : false
if (enableServiceWorker && serviceWorkerAvailable) {
const wb = new Workbox('/service-worker.js');
let registration;
const showSkipWaitingPrompt = (event) => {
if (window.confirm("New version available! Refresh?")) {
wb.addEventListener('controlling', (event) => {
window.location.reload();
});
console.log('registration', registration) //<-- LINE 13
// In Chrome and Edge this logs a service worker registration object
// In Firefox, this is undefined !!?
if (registration && registration.waiting) {
messageSW(registration.waiting, {type: 'SKIP_WAITING'});
}
}
}
// Add an event listener to detect when the registered service worker has installed but is waiting to activate.
wb.addEventListener('waiting', showSkipWaitingPrompt);
wb.addEventListener('externalwaiting', showSkipWaitingPrompt);
wb.register().then((r) => {
registration = r
console.log('Service worker registered', registration) //<-- LINE 23
}).catch(registrationError => {
console.error('Service worker error', registrationError )
})
}
// Install prompt event handler
export let deferredPrompt
window.addEventListener('beforeinstallprompt', (event) => {
event.preventDefault() // Prevent Chrome 76 and later from showing the mini-infobar
deferredPrompt = event // Stash the event so it can be triggered later.
// Update UI notify the user they can add to home screen
try{
showInstallPromotion()
}catch(e){
// console.log('showInstallPromotion()', e)
}
})
window.addEventListener('appinstalled', (event) => {
console.log('a2hs installed')
})
In Firefox dev-tools I can see the new service worker precache, but all other cache belongs to previous version. After shift-reload the new service worker gets "fully activated".
How can I get Firefox to hard reload the page automatically after new service worker install?
UPDATE: It seems like Firefox is missing a handle to the service worker on line 13 of app-js.
UPDATE: Console output indicates that the code sequence differs between browsers?
Chrome / Edge
registration > ServiceWorkerRegistration {installing: null, waiting: ServiceWorker, active: ServiceWorker, navigationPreload: NavigationPreloadManager, scope: "http://127.0.0.1:8080/", …} app.js:13
**PAGE RELOAD***
Service worker registered ServiceWorkerRegistration {installing: null, waiting: null, active: ServiceWorker, navigationPreload: NavigationPreloadManager, scope: "http://127.0.0.1:8080/", …} app.js:23
Firefox
registration undefined app.js:13:14
Service worker registered > ServiceWorkerRegistration { installing: null, waiting: ServiceWorker, active: ServiceWorker, scope: "http://127.0.0.1:8080/", updateViaCache: "imports", onupdatefound: null, pushManager: PushManager } app.js:23:12
Kind regards /K
This might help you , please check controllerchange of serviceworker.
As per this documentations:- The oncontrollerchange property of the ServiceWorkerContainer interface is an event handler fired whenever a "controllerchange event occurs" — when the document's associated ServiceWorkerRegistration acquires a new active worker.
To use it, you can attach an event handler and it will be triggered only when a new service worker activates. and If you want you can reload the page using reload function.
navigator.serviceWorker.addEventListener('controllerchange', function(){
window.location.reload();
});
I created a special case since Firefox seems to install the new service-worker differently from chromium (does not have a handle to the service-worker registration on line 13)
When the new service worker is waiting showSkipWaitingPrompt gets triggered and
in Chromium the service-worker registration is ready ---> we call SKIP_WAITING --> the browser reloads and replaces the service worker
in Firefox the service-worker registration handle is not accessible yet --> we cannot call SKIP_WAITING
The solution, for me, was to add the below line in the registration. This tells Firefox to skip waiting when the new service-worker is in waiting state and we have a registration handle.
wb.register().then((r) => {
registration = r
if(registration.waiting){ mySkipWaitingNow() } // Fix for firefox
...
The mySkipWaitingNow() tells the service-worker to SKIP_WAITING without prompting the user.
This will never trigger in Chrome/Edge since the browser reloads in showSkipWaitingPrompt() - see point 1 above.
To prevent a possible eternal loop I also created a global variable skipWaitingConfirmed that gets set in showSkipWaitingPrompt() and checked in mySkipWaitingNow().
/K

How to detect microphone permissions state

I'm looking to show different modals to the user to guide them to allow their permission when trying to use a microphone requiring task in my app - based on whether they have not yet been requested for microphone permissions, granted permissions, or blocked permissions.
How can I reliably find out which state of permissions the user is in? Preferably with an onChange handler to know if the user revokes their permission at any point and something that would work for Chrome, Firefox, and Safari.
The inspiration comes from voice.google.com where they show different screen on the call widget when trying to call the Google voice number with the microphone permission set to "ask", "allow", or "block".
I've gone through https://developers.google.com/web/updates/2015/04/permissions-api-for-the-web but I can't seem to find a solution for how I would like to implement this.
Edit:
<div id="button">Click me</div>
<script>
const button = document.getElementById("button")
button.addEventListener('click', () => {
navigator.mediaDevices.getUserMedia({
audio: true
})
.then(function(stream) {
console.log('You let me use your mic!')
console.log(stream)
navigator.permissions.query({
name: 'microphone'
}, ).then(function(permissionStatus) {
console.log("PermissionStatus: ", permissionStatus.state); // granted, denied, prompt
permissionStatus.onchange = function() {
console.log("Permission changed to " + this.state);
}
})
})
.catch(function(err) {
console.error(err)
console.log('No mic for you!')
});
})
navigator.permissions.query({
name: 'microphone'
})
.then(function(permissionStatus) {
console.log("PermissionStatus: ", permissionStatus.state); // granted, denied, prompt
permissionStatus.onchange = function() {
console.log("Permission changed to " + this.state);
}
})
</script>
The name is "microphone" and you'll get "granted", "prompt" or "denied" as the state.
https://developers.google.com/web/fundamentals/media/recording-audio#use_the_permissions_api_to_check_if_you_already_have_access

chrome notification doesn't display and return error

I got a problem with API notification with Chrome. It did not manage to display the notification and return an error into the console. I did not experience this problem with Firefox.
index.js
document.addEventListener('DOMContentLoaded', function () {
Notification.requestPermission(function (status) {
console.log('notification permission status: ', status);
displayNotification();
});
function displayNotification() {
if (Notification.permission === 'granted') {
navigator.serviceWorker.getRegistration()
.then(function (reg) {
var options = {
body: 'Buzz! Buzz!',
icon: '/static/img/logo_menu.png',
vibrate: [200, 100, 200, 100, 200, 100, 200],
tag: 'vibration-sample'
};
reg.showNotification('Hello world !', options);
})
}
}
}, false);
Error message
notification permission status: granted index.js:25
Uncaught (in promise) TypeError: Cannot read property 'showNotification' of undefined at index.js:25
Someone know why I experience this problem only on Chrome ?
Edit :
I didn't mention but I have a script tag :
<script>
// Check that service workers are registered
if ('serviceWorker' in navigator) {
// Use the window load event to keep the page load performant
window.addEventListener('load', () => {
navigator.serviceWorker.register('sw.js');
});
}
</script>
that root to sw.js file :
importScripts('https://storage.googleapis.com/workbox-cdn/releases/3.4.1/workbox-sw.js');
if (workbox) {
console.log(`Yay! Workbox is loaded 🎉`);
} else {
console.log(`Boo! Workbox didn't load 😬`);
}
I solve the problem in addition to index.js setTimeout(). By this way, I'm waiting for that service worker working to start to read my file.

Firefox TypeError: ServiceWorker script encountered an error during installation

I'm developing web push notification on my website. I follow the Web Push Notifications of Google and The Service Worker Cookbook of Mozilla.
I have tested on the Google Chrome v50+ and everything is working but I will get the error below on Firefox 44, 45, 46, 52, latest Firefox (version 57.0.4 64 bit) when calling navigator.serviceWorker.register('./push-service-worker.js') function.
TypeError: ServiceWorker script at http://localhost:9600/push-service-worker.js for scope http://localhost:9600/ encountered an error during installation.
This is my code:
Register ServiceWorker in controller.js
navigator.serviceWorker.register('push-service-worker.js')
.then((registration) => {
return registration.pushManager.getSubscription()
.then((subscription) => {
if (subscription) {
return subscription;
}
var subscribeOptions = {
userVisibleOnly: true,
applicationServerKey: buildApplicationServerKey(),
};
return registration.pushManager.subscribe(subscribeOptions);
});
})
.then((subscription) => {
sendSubscriptionToServer(subscription);
})
.catch((err) => {
console.log('Unable to subscribe to push: ', err);
});
push-service-worker.js
'use strict';
self.addEventListener('push', (event) => {
var payload = event.data.json();
var title = payload.title || 'Title';
event.waitUntil(
self.registration.showNotification(title, {
body: payload.body,
icon: './common/images/notification-icon-192x192.png',
image: payload.image || '',
})
);
});
self.addEventListener('notificationclick', (event) => {
event.notification.close();
var urlToOpen = new URL('/', self.location.origin).href;
event.waitUntil(
clients.matchAll({
type: 'window',
includeUncontrolled: true,
})
.then((windowClients) => {
var matchingClient = null;
for (var i = 0; i < windowClients.length; i++) {
var windowClient = windowClients[i];
if (windowClient.url === urlToOpen) {
matchingClient = windowClient;
break;
}
}
if (matchingClient) {
return matchingClient.focus();
} else {
return clients.openWindow(urlToOpen);
}
})
);
});
Directory structure
./root
---- manifest.json
---- push-service-worker.js
---- src
---- controller.js
Thank for helping!
As wanderview said at here:
FWIW, you should always use a separate profile for each channel (release/beta/dev-edition/nightly). We're working on making it work like that out-of-the-box, but its not ready yet.
This problem is encountered when I use one profile for multiple Firefox version. To fix this issue go to about:support and click Refresh Firefox. If it doesn't work, you can go to about:profiles, click Create new profile, and then Launch profile in new browser.
In my case, this was caused by Firefox not being able to access the serviceworker.js file.
The server I was hosting it on had a permissions check based on cookies, and in this case Firefox was not sending the cookie as I believe it was considered a cross-site script.
On the server, I made the serviceworker.js file accessible publicly, and then Firefox could register the service worker.
It was particularly difficult to notice this, because the Firefox Developer Tools did not show the Forbidden response in the Console, nor did it even show any request for serviceworker.js in the Network tab.
Therefore, presumably the TypeError is in fact a generic error and should be read as 'something went wrong registering the Service Worker'.

Chrome push notification - how to open URL adress after click?

I am new to Google Chrome Push notifications and I was just reading some questions and answers here, on stackoverflow and I have ended with this easy push notification javascript.
navigator.serviceWorker.register('sw.js');
function notify() {
Notification.requestPermission(function(result) {
if (result === 'granted') {
navigator.serviceWorker.ready.then(function(registration) {
registration.showNotification('test notification', {
body: 'Hey I am test!',
icon: 'image.png',
});
});
}
});
}
Its just simple notification, but I need open a new window with other webpage after click on notification.
I know it is possible, but I cant find examples using "serviceWorker" syntax.
Please help. Thanks.
I am guessing you are in a Service Worker context, because that's where Push Notifications are received. So you have the self object to add a event listener to, that will react to a click on the notification.
(Place this code in your sw.js file, which is your Service Worker script.)
self.addEventListener('notificationclick', function(event) {
let url = 'https://example.com/some-path/';
event.notification.close(); // Android needs explicit close.
event.waitUntil(
clients.matchAll({type: 'window'}).then( windowClients => {
// Check if there is already a window/tab open with the target URL
for (var i = 0; i < windowClients.length; i++) {
var client = windowClients[i];
// If so, just focus it.
if (client.url === url && 'focus' in client) {
return client.focus();
}
}
// If not, then open the target URL in a new window/tab.
if (clients.openWindow) {
return clients.openWindow(url);
}
})
);
});
If you want to open website with dynamic URL received from FCM push notification or any other web push notification then
BELOW IS AN EXAMPLE OF SERVICE WORKER USED FOR FCM PUSH NOTIFICATION
messaging.setBackgroundMessageHandler(function(payload) {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
// Customize notification here
var notificationTitle = payload.data.title; //or payload.notification or whatever your payload is
var notificationOptions = {
body: payload.data.body,
icon: payload.data.icon,
data: { url:payload.data.click_action }, //the url which we gonna use later
actions: [{action: "open_url", title: "Read Now"}]
};
return self.registration.showNotification(notificationTitle,
notificationOptions);
});
and handle click event with below code
self.addEventListener('notificationclick', function(event) {
switch(event.action){
case 'open_url':
clients.openWindow(event.notification.data.url); //which we got from above
break;
case 'any_other_action':
clients.openWindow("https://www.example.com");
break;
}
}
, false);
Hope it helps!
(This code refers to firebase messaging) I was also searching for a soluting and the answer was very easy, but there was no doc saying it clearly. You need to put "click_action" = "your url" inside the notification json. Here is an example:
notification: {
title: "Come",
icon: '../../../../assets/logo.png',
vibrate: [300,100,400,100,400,100,400],
body: "some text",
click_action : "your link"
}
Hope it helps.
{
"notification": {
"title": "Hey there",
"body": "Subscribe to might ghost hack youtube channel",
"click_action" : "http://localhost:4200"
},
"to":"YOUR_TOKEN"
}
This worked for me
"#angular/fire": "^6.1.5",
"firebase": "^7.0 || ^8.0"

Categories