So I have managed to get push notifications to work. The problem is that whilst they appear on my screen, they don't seem to be triggering the push event in my serviceworker. I am trying to console.log something once the event is triggered but it's not working. Does anyone know what could be causing this problem? (Service worker is registered properly).
self.addEventListener('push', function (e) {
console.log('hello world');
if (!(self.Notification && self.Notification.permission ===
'granted')) {
//notifications aren't supported or permission not granted!
return;
}
if (e.data) {
var msg = e.data.json();
console.log(msg)
e.waitUntil(self.registration.showNotification(msg.title, {
body: msg.body,
icon: msg.icon,
actions: msg.actions
}));
}
});
After writing this I discovered that the push event works correctly on my phone but not on my laptop.
Related
I have a page where I want my script to let the server know that the user went offline when the browser/tab/page is closed.
$(document).ready(function() {
// Inform the server that the user went online
setStatus('online');
// Inform the server that the user went offline
$(window).on('beforeunload', function() {
setStatus('offline');
});
// Inform the server that the user went offline
$(window).on('unload', function() {
setStatus('offline');
});
});
async function setStatus(status) {
let { data } = await axios.post('/status', { status: status });
}
The part where I have setStatus('online'); works but I can never seem to set the status to offline when I close the page/tab/browser.
Looking around, I found this really nice answer - I just had to put this on my server.js file:
peer.oniceconnectionstatechange = function() {
if(peer.iceConnectionState == 'disconnected') {
console.log('Disconnected');
}
}
It does take a few seconds before it registers that the peer has dropped but at least it's always firing and that's reliable enough for me.
I'm hoping to migrate from using WebUSB to SerialAPI (which is explained nicely here).
Current Code:
try {
let device = await navigator.usb.requestDevice({
filters: [{
usbVendorId: RECEIVER_VENDOR_ID
}]
})
this.connect(device)
} catch (error) {
console.log(DEVICE_NAME + ': Permission Denied')
}
New Code:
try {
let device = await navigator.serial.requestPort({
filters: [{
usbVendorId: RECEIVER_VENDOR_ID
}]
})
this.connect(device)
} catch (error) {
console.log(DEVICE_NAME + ': Permission Denied')
}
The new code appears to work, but I think it's because the browser has already requested the device via the old code.
I've tried restarting Chrome as well as clearing all of the browsing history. Even closed the USB-claiming page and claimed the device with another app (during which it returns the DOMException: Unable to claim interface error), but Chrome doesn't seem to want to ask again. It just happily streams the data with the previous connection.
My hope was that using SerialAPI would be a way to avoid fighting over the USB with other processes, or at least losing to them.
Update
I had forgotten about:
Failed to execute 'requestPort' on 'Serial': "Must be handling a user gesture to show a permission request"
Does this mean that the user will need to use a button to connect to the device via SerialUSB? I think with WebUSB I was able to make the connect window automatically pop up.
For both APIs, as is noted in the update, a user gesture is required in order to call the requestDevice() or requestPort() method. It is not possible to automatically pop up this prompt. (If there is that's a bug so please let the Chrome team know so we can fix it.)
Permissions granted to a site through the WebUSB API and Web Serial API are currently tracked separately so permission to access a device through one will not automatically translate into the other.
There is not currently a way to programatically forget a device permission. That would require the navigator.permissions.revoke() method which has been abandoned. You can however manually revoke permission to access the device by clicking on the "lock" icon in the address bar while visiting the site or going to chrome://settings/content/usbDevices (for USB devices) and chrome://settings/content/serialPorts (for serial ports).
To get Chrome to "forget" the WebUSB device previously paired via navigator.usb.requestDevice API:
Open the page paired to the device you want to forget
Click on the icon in the address bar
Click x next to device. If nothing is listed, then there are no paired devices for this web page.
The new code was NOT working. It just appeared to be because Chrome was already paired with the port via the old code. There is no way the "new code" could have worked because, as noted in Kalido's comment, the SerialAPI (due to its power) requires a user gesture to connect.
The code I'm using to actually connect and get data is basically built up from a few pieces from the above links in the OP:
navigator.serial.addEventListener('connect', e => {
// Add |e.target| to the UI or automatically connect.
console.log("connected");
});
navigator.serial.addEventListener('disconnect', e => {
// Remove |e.target| from the UI. If the device was open the disconnection can
// also be observed as a stream error.
console.log("disconnected");
});
console.log(navigator.serial);
document.addEventListener('DOMContentLoaded', async () => {
const connectButton = document.querySelector('#connect') as HTMLInputElement;
if (connectButton) {
connectButton.addEventListener('click', async () => {
try {
// Request Keiser Receiver from the user.
const port = await navigator.serial.requestPort({
filters: [{ usbVendorId: 0x2341, usbProductId: not_required }]
});
try {
// Open and begin reading.
await port.open({ baudRate: 115200 });
} catch (e) {
console.log(e);
}
while (port.readable) {
const reader = port.readable.getReader();
try {
while (true) {
const { value, done } = await reader.read();
if (done) {
// Allow the serial port to be closed later.
reader.releaseLock();
break;
}
if (value) {
console.log(value);
}
}
} catch (error) {
// TODO: Handle non-fatal read error.
console.log(error);
}
}
} catch (e) {
console.log("Permission to access a device was denied implicitly or explicitly by the user.");
console.log(e);
console.log(port);
}
}
}
The device-specific Vendor and Product IDs would obviously change from device to device. In the above example I have inserted an Arduino vendor ID.
It doesn't answer the question of how to get Chrome to "forget", but I'm not sure if that's relevant when using SerialAPI because of the required gesture.
Hopefully someone with more experience will be able to post a more informative answer.
Firebase Cloud Messaging
I have everything setup, the Push messages are received fine and when I click on it, it opens new window... but only in Chrome, in Firefox it is not opened.
I have specifically allowed popups, but didn't make any difference.
I was just debugging for 1 hour
self.addEventListener('notificationclick', function(e) {
console.log("This is printed to console fine");
clients.openWindow('https://example.com'); // this makes no error, nothing
});
Any ideas?
Works in Firefox 47.0
Doesn't work in Firefox Quantum 60
Subscribed a bug.
I also struggled with this for a while...
To anyone else having the problem, I wanted to add something:
You can still use firebase.messaging() but you have to make sure you put it AFTER the event listener.
To make this clear:
This does NOT work (clients.openWindow does nothing):
const messaging = firebase.messaging();
// Add an event listener to handle notification clicks
self.addEventListener('notificationclick', function (event) {
if (event.action === 'close') {
event.notification.close();
} else if (event.notification.data.target_url && '' !== event.notification.data.target_url.trim()) {
clients.openWindow(event.notification.data.target_url);
}
});
messaging.setBackgroundMessageHandler(function (payload) {
// ... add custom notification handling ...
});
This does work (clients.openWindow opens link as expected):
// Add an event listener to handle notification clicks
self.addEventListener('notificationclick', function (event) {
if (event.action === 'close') {
event.notification.close();
} else if (event.notification.data.target_url && '' !== event.notification.data.target_url.trim()) {
clients.openWindow(event.notification.data.target_url);
}
});
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(function (payload) {
// ... add custom notification handling ...
});
Still not sure of the underlying reason. I also suspect that the messaging() messes up the click event and makes Firefox refuse to open a window because it considers that the user took no direct action at the notification.
But at least I have a workaround and can keep going.
Hope that helps.
I removed from the service worker:
const messaging = firebase.messaging();
And it is now working.
This is just nuts.
Found out the reason of this.
The Firebase.messaging was the culprit ONLY if you send the messaging payload (from server) as such:
{
"notification": {
//...
}
}
The existence of notification property will prevent the notificationclick from propagating due to some reason in the firebase sdk.
You can send the following instead
{
"data": {
//...
}
}
This is the code, P.S: the service httpd in s10 is stopped
try {
var source = new EventSource("http://s10/server.php");
console.log(source);
} catch (e) {
console.log("ADSfasfasfasdfasdfas" + e)
}
this is the console:
why the heck the try catch is not catching the error ??
ofcourse I have onerror event and onclose event:
source.addEventListener('error', function(e) {
if (source.readyState == 2) {
connectionClosed();//to change some style
console.log("Disconnected");
}
}, false);
source.onerror = function(e) {
if (source.readyState != 0) {
connectionClosed();//to change some style
console.log("Disconnected");
}
};
source.onclose = function() {
connectionClosed();//to change some style
console.log('Connection closed');
}
When I run this in the Console on an arbitrary URL that isn't set up to handle SourceEvents, the Console successfully logs a SourceEvent instance. This is similar to what you're describing.
So my guess here is that there's no error to catch. The SourceEvent instance is successfully constructed, even though there's no actual connection. If you want to detect if the connection is working, use the EventSource.readyState property.
In my screenshot you can see that readyState = 0, which means that the connection is "connecting", but in reality it's never going to finish connecting because the server on the other side isn't set up to handle SourceEvents.
https://developer.mozilla.org/en-US/docs/Web/API/EventSource/readyState
I'm testing the same thing, the onerror doesn't provide really useful info. For example, if you shut down your server or if you send an empty response while your client is connected, you can't detect programmatically on the client side the specific cause, I can see that just inspecting the dev console.
I have a content script which times how long a user views a page. To do this, I inject a content script into each page, start a timer and then emit a message back to the add-on when the onbeforeunload event is triggered.
The message never seems to get passed to the background script however.
Given that my main.js looks like this:
var pageMod = require('page-mod'),
self = require("self");
pageMod.PageMod({
include: "http://*",
contentScriptFile: [self.data.url('jquery.min.js'),
self.data.url('content.js')],
onAttach: function(worker) {
worker.port.on('pageView', function(request) {
console.log("Request received");
});
}
});
I can send a message to main.js using the following code no problem.
self.port.emit('pageView', { visitTime: time });
I run into a problem when I try to do it as the user leaves the page however. The message is never received when I do it like this:
$(window).bind('onbeforeunload', function(e) {
self.port.emit('pageView', { visitTime: time });
// This should prevent the user from seeing a dialog.
return undefined;
});
I've tried listening for beforeunload too, that doesn't work either. What could be the problem?
The window object that content scripts access in Firefox browser add-ons is a proxy object and can be a little temperamental. Using window.addEventListener will work.
window.addEventListener('beforeunload', function(e) {
# Do stuff then return undefined so no dialog pops up.
return undefined
});
The onbeforeUnload event is not synchronous, so the browser garbage collects the page before it is finished. Use a synchronous AJAX request:
function Data()
{
var client = new XMLHttpRequest();
client.open("GET", "/request", false); // third paramater indicates sync xhr
client.setRequestHeader("Content-Type", "text/plain;charset=UTF-8");
client.send({ visitTime: time });
client.onreadystatechange = emitter;
}
function emitter()
{
self.port.emit('pageView', { visitTime: time });
}
or return a string as an alternative.