Is it possible to extend desktop notification with javascript? - javascript

I was making a web chat app with firebase. Everything is good including the notification but the notification only shows up for 5 seconds. What I want to do is to extend the notification show duration.
In this examples, there is requireInteraction function but this function does not extend the show duration but it prevent the notification to hide.
Here is my code
Notification.requestPermission().then(function(permission) {
if (permission === 'granted') {
var config = {
apiKey: "apikey",
messagingSenderId: "senderid",
}
firebase.initializeApp(config)
const messaging = firebase.messaging();
messaging.usePublicVapidKey('PublicVapidKey');
messaging.getToken().then(function(currentToken) {
if (currentToken) {
doFCMReg(currentToken)
} else {
console.log('No Instance ID token available. Request permission to generate one.')
}
}).catch(function(err) {
console.log('An error occurred while retrieving token. ', err)
})
messaging.onTokenRefresh(function() {
messaging.getToken().then(function(refreshedToken) {
doFCMReg(token)
}).catch(function(err) {
console.log('Unable to retrieve refreshed token ', err)
})
})
messaging.onMessage(function(payload) {
var n = payload['notification']
var d = payload['data']
var not = new Notification("𝗦𝗦𝗧 \n"+xad(n['body']), { icon: "assets/icon/png/SST-icon-512.png", tag: "" })
switch(d['type']) {
case "newchat":
getInvitation()
refreshChat(xad(d['phoneSender']), xad(d['invitationId']))
var target = xad(localStorage.phone)==xad(d['phoneSender']) ? xad(d['phoneReceiver']) : xad(d['phoneSender'])
not.onclick = function() {
if(localStorage.currentPage!='page-chat') {
switchFragment('#page-chat', 'Chats')
}
$('#invitation-'+target).trigger('click')
}
break
case "newcontact":
getContacts()
not.onclick = function() {
if(localStorage.currentPage!='page-contact') {
switchFragment('#page-contact', 'Contacts')
}
$( '#btn-request').trigger('click')
$(".contact[data-phone='"+d['senderPhone']+"']").trigger('click')
}
break
}
})
} else {
alert("SST needs Notifications to be allowed to enable push notification.")
}
})
Is there any way to extend the show duration?

Related

gapi.client.getToken() always return null when refresh ReactJS app

So I am making a React app that accesses the user's Google calendar and fetch their events.
It is working fine, but the problem is that when I refresh the app, the user has to consent again, which I would like to avoid.
Is there a way for me to skip the consent, after the first time the user consent already?
On user click to log in
function handleAuthClick() {
tokenClient.callback = async (resp) => {
if (resp.error !== undefined) {
throw (resp);
}
};
if (gapi.client.getToken() === null) {
// Prompt the user to select a Google Account and ask for consent to share their data
// when establishing a new session.
tokenClient.requestAccessToken({prompt: 'consent', access_type:"offline"});
} else {
// Skip display of account chooser and consent dialog for an existing session.
tokenClient.requestAccessToken({prompt: ''});
}
}
Some other function that is used to log in that I copy from google's documentation
function gapiLoaded() {
try{
gapi.load('client', intializeGapiClient);
} catch {
console.log("Not Logged in");
}
}
/**
* Callback after the API client is loaded. Loads the
* discovery doc to initialize the API.
*/
async function intializeGapiClient() {
var API_KEY = "x";
fetch("http://localhost:5050/id").then(resp => {
// fetch("https://calendar-342103.uc.r.appspot.com/id").then(resp => {
return resp.json();
}).then((data) => {
API_KEY = data.val2;
}).then(async () => {
try{
await gapi.client.init({
apiKey: API_KEY,
discoveryDocs: [DISCOVERY_DOC],
});
gapiInited = true;
maybeEnableButtons();
} catch {
console.log("User not logged in")
}
})
}
/**
* Callback after Google Identity Services are loaded.
*/
function gisLoaded() {
var CLIENT_ID = "x";
fetch("http://localhost:5050/id").then(resp => {
// fetch("https://calendar-342103.uc.r.appspot.com/id").then(resp => {
return resp.json();
}).then((data) => {
CLIENT_ID = data.val1;
}).then(() => {
try {
tokenClient = google.accounts.oauth2.initTokenClient({
client_id: CLIENT_ID,
scope: SCOPES,
callback: '', // defined later
});
gisInited = true;
maybeEnableButtons();
} catch {
}
})
}

Angular 8 FCM receiveMessage() not working when application run in background

Currently i am using Firebase cloud messaging for webs push notification in angular.I want to use custom notification and sound for background message like foreground message but I am not sure how i customize background notification please help.
firebase-messaging-sw.js file code
const messaging = firebase.messaging();
messaging.onBackgroundMessage(function (payload) {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
var data = payload.data;
});
self.addEventListener('push', function (event) {
const data = JSON.parse(event.data.text());
console.log(data);
event.waitUntil(self.registration.showNotification(data.data.title, {"body": data.data.body}));
// event.waitUntil(self.registration.showNotification(event.notification.title, {"body": event.notification.body}));
});
messaging.service.ts code
import { Injectable } from '#angular/core';
import { AngularFireMessaging } from '#angular/fire/messaging';
import { BehaviorSubject } from 'rxjs'
#Injectable()
export class MessagingService {
currentMessage = new BehaviorSubject(null);
constructor(private angularFireMessaging: AngularFireMessaging) {
this.angularFireMessaging.messaging.subscribe(
(_messaging) => {
_messaging.onMessage = _messaging.onMessage.bind(_messaging);
_messaging.onTokenRefresh = _messaging.onTokenRefresh.bind(_messaging);
}
)
}
requestPermission() {
this.angularFireMessaging.requestToken.subscribe(
(token) => {
console.log(token);
},
(err) => {
console.error('Unable to get permission to notify.', err);
}
);
}
receiveMessage() {
this.angularFireMessaging.messages.subscribe(
(payload) => {
console.log("new message received. ", payload);
self.audio.play();
this.currentMessage.next(payload);
// this.showCustomNotification(payload);
})
}

Unable to create a stable websocket implementation

Usecase:
This runs on the server side (Keystone) of an Android application
App connects to the socket with the user's accesstoken
App shows indicators for all the other user's who are connected to the socket
When a user changes some data in the app, a force refresh is send over the socket to all the "online" users so that they know to fetch the latest data
Main problem:
It works until a client loses it's internet connection right in between the intervals. Then the socket connection is closed and not reopened.
I don't know if it's a problem with my implementation or a problem with implementation on the client side
Implementation uses:
https://github.com/websockets/ws
More specifically https://github.com/websockets/ws#how-to-detect-and-close-broken-connections
Here is the implementation on the server:
const clients = {};
let wss = null;
const delimiter = '_';
/**
* Clients are stored as "companyId_deviceId"
*/
function getClients() {
return clients;
}
function sendMessage(companyId, msg) {
try {
const clientKey = Object.keys(clients).find((a) => a.split(delimiter)[0] === companyId.toString());
const socketForUser = clients[clientKey];
if (socketForUser && socketForUser.readyState === WebSocket.OPEN) {
socketForUser.send(JSON.stringify(msg));
} else {
console.info(`WEBSOCKET: could not send message to company ${companyId}`);
}
} catch (ex) {
console.error(`WEBSOCKET: could not send message to company ${companyId}: `, ex);
}
}
function noop() { }
function heartbeat() {
this.isAlive = true;
}
function deleteClient(clientInfo) {
delete clients[`${clientInfo.companyId}${delimiter}${clientInfo.deviceId}`];
// notify all clients
forceRefreshAllClients();
}
function createSocket(server) {
wss = new WebSocket.Server({ server });
wss.on('connection', async (ws, req) => {
try {
// verify socket connection
let { query: { accessToken } } = url.parse(req.url, true);
const decoded = await tokenHelper.decode(accessToken);
// add new websocket to clients store
ws.isAlive = true;
clients[`${decoded.companyId}${delimiter}${decoded.deviceId}`] = ws;
console.info(`WEBSOCKET: ➕ Added client for company ${decoded.companyId} and device ${decoded.deviceId}`);
await tokenHelper.verify(accessToken);
// notify all clients about new client coming up
// including the newly created socket client...
forceRefreshAllClients();
ws.on('pong', heartbeat);
} catch (ex) {
console.error('WEBSOCKET: WebSocket Error', ex);
ws.send(JSON.stringify({ type: 'ERROR', data: { status: 401, title: 'invalid token' } }));
}
ws.on('close', async () => {
const location = url.parse(req.url, true);
const decoded = await tokenHelper.decode(location.query.accessToken);
deleteClient({ companyId: decoded.companyId, deviceId: decoded.deviceId });
});
});
// Ping pong on interval will remove the client if the client has no internet connection
setInterval(() => {
Object.keys(clients).forEach((clientKey) => {
const ws = clients[clientKey];
if (ws.isAlive === false) return ws.terminate();
ws.isAlive = false;
ws.ping(noop);
});
}, 15000);
}
function forceRefreshAllClients() {
setTimeout(function () {
Object.keys(clients).forEach((key) => {
const companyId = key.split(delimiter)[0];
sendMessage(companyId, createForcedRefreshMessage());
});
}, 1000);
}

nextPageToken doesn't change

I am trying to use the node.js Google API to iterate over my email messages
const google = require('googleapis');
The problem is that I keep getting the same email messages and every response contains the exact same nextPageToken.
I also noticed that trying to control the maxResults parameter has no effect. I tried to add them to the options object and the query objects and it had no effect in either way.
function listThreads(auth, nextPageToken) {
const gmail = google.gmail('v1');
const query = {
auth : auth,
userId: 'me',
q: ""
};
const options = {maxResults: 20}
if (nextPageToken) {
query.pageToken = nextPageToken;
}
gmail.users.messages.list(query, options, function(err, response) {
if (err) {
console.log('The API returned an error: ' + err);
return;
}
const messages = response.messages;
if (messages.length === 0) {
console.log('No threads found.');
} else {
for (let i = 0; i < messages.length; i++) {
const message = messages[i];
gmail.users.messages.get({
auth : auth,
userId: 'me',
id : message.id
}, function(err, response) {
if (err) {
logger.error("Failed to get email", err);
} else {
const parser = new Parser();
parser.parse(response.payload, response.labelIds);
}
});
}
}
if (response.nextPageToken) {
listThreads(auth, response.nextPageToken);
}
});
}
What am I doing wrong?

Receiving one notification for each tab in browser in foreground with FCM

I'm using FCM API to receive push notifications from browser. The firebase-messaging-sw.js works as expected and messaging.setBackgroundMessageHandler fires only once when the web app is in background. However, when the app is in foreground, I'm receiving one notification for each browser tab (if I have the app opened in 3 tabs, I receive 3 notifications). I wonder how should I handle this, since I can't find any reference to this issue. This is the code for FCM messages in the foreground:
import NotificationActionCreators from '../actions/NotificationActionCreators';
import NotificationService from './NotificationService';
import LocalStorageService from './LocalStorageService';
import { FIREBASE_SCRIPT, FCM_URL, FCM_API_KEY, FCM_AUTH_DOMAIN, FCM_PROJECT_ID, FCM_SENDER_ID, PUSH_PUBLIC_KEY } from '../constants/Constants';
class ServiceWorkerService {
constructor() {
this._messaging = null;
this._subscriptionData = null;
}
// This function is called once
init() {
this.loadScript(FIREBASE_SCRIPT, () => this.onFirebaseLoaded());
}
onFirebaseLoaded() {
// Initialize Firebase
let config = {
apiKey: FCM_API_KEY,
authDomain: FCM_AUTH_DOMAIN,
projectId: FCM_PROJECT_ID,
messagingSenderId: FCM_SENDER_ID
};
firebase.initializeApp(config);
this._messaging = firebase.messaging();
this.requestPermission();
// Callback fired if Instance ID token is updated.
this._messaging.onTokenRefresh(() => {
this._messaging.getToken()
.then((refreshedToken) => {
console.log('Token refreshed.');
NotificationActionCreators.unSubscribe(this._subscriptionData).then(() => {
// Indicate that the new Instance ID token has not yet been sent to the
// app server.
this.setTokenSentToServer(false);
// Send Instance ID token to app server.
this.sendTokenToServer(refreshedToken);
}, () => console.log('Error unsubscribing user'));
})
.catch(function(err) {
console.log('Unable to retrieve refreshed token ', err);
});
});
// Handle incoming messages.
// *** THIS IS FIRED ONCE PER TAB ***
this._messaging.onMessage(function(payload) {
console.log("Message received. ", payload);
const data = payload.data;
NotificationActionCreators.notify(data);
});
}
requestPermission() {
console.log('Requesting permission...');
return this._messaging.requestPermission()
.then(() => {
console.log('Notification permission granted.');
this.getToken();
})
.catch(function(err) {
console.log('Unable to get permission to notify.', err);
});
}
getToken() {
// Get Instance ID token. Initially this makes a network call, once retrieved
// subsequent calls to getToken will return from cache.
return this._messaging.getToken()
.then((currentToken) => {
if (currentToken) {
this.sendTokenToServer(currentToken);
} else {
// Show permission request.
console.log('No Instance ID token available. Request permission to generate one.');
this.setTokenSentToServer(false);
}
})
.catch(function(err) {
console.log('An error occurred while retrieving token. ', err);
this.setTokenSentToServer(false);
});
}
sendTokenToServer(currentToken) {
const subscriptionData = {
endpoint: FCM_URL + currentToken,
platform: 'Web'
};
if (!this.isTokenSentToServer()) {
console.log('Sending token to server...');
this.updateSubscriptionOnServer(subscriptionData);
} else {
console.log('Token already sent to server so won\'t send it again ' +
'unless it changes');
}
this._subscriptionData = subscriptionData;
}
isTokenSentToServer() {
return LocalStorageService.get('sentToServer') == 1;
}
setTokenSentToServer(sent) {
LocalStorageService.set('sentToServer', sent ? 1 : 0);
}
updateSubscriptionOnServer(subscriptionData) {
if (subscriptionData) {
NotificationActionCreators.subscribe(subscriptionData);
this.setTokenSentToServer(true);
this._subscriptionData = subscriptionData;
} else {
console.log('Not subscribed');
}
}
unSubscribe() {
this.removeSetTokenSentToServer();
return this._messaging.getToken()
.then((currentToken) => {
return this._messaging.deleteToken(currentToken)
.then(() => {
console.log('Token deleted.');
return NotificationActionCreators.unSubscribe(this._subscriptionData);
})
.catch(function(err) {
console.log('Unable to delete token. ', err);
return new Promise(function(resolve, reject) {
reject(error)
});
});
})
.catch(function(err) {
console.log('Error retrieving Instance ID token. ', err);
return new Promise(function(resolve, reject) {
reject(error)
});
});
}
}
removeSetTokenSentToServer() {
LocalStorageService.remove('sentToServer');
}
loadScript = function (url, callback) {
let head = document.getElementsByTagName('head')[0];
let script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
script.onload = callback;
// Fire the loading
head.appendChild(script);
}
}
Is there any way to show the notification just for the first tab found?
The only way I've found to achieve this is to check and set a "notification id" variable in the local storage with a setTimeout with a random time:
this._messaging.onMessage(function(payload) {
const data = payload.data;
// This prevents to show one notification for each tab
setTimeout(() => {
if (localStorage.getItem('lastNotificationId')) != parseInt(data.notId)) {
localStorage.setItem('lastNotificationId', parseInt(data.notId))
NotificationActionCreators.notify(data);
}
}, Math.random() * 1000);
});
The notId is sent within the push notification and is an identifier for the notification.
One way is to create a unique variable per tab, say Math.random() or (new Date()).getMilliseconds() and store that on the server with the token. Now the server can target each tab by attaching the variable to the message, with each tab checking the message variable before acting.
To reduces odds of targeting a closed tab, send the variable up with each request, so the server always targets the latest one.
use document.hidden to detect active tab
if (!document.hidden) {
NotificationActionCreators.notify(data);
}

Categories

Resources