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

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);
})
}

Related

Spring RSocket Security + RSocket-WebSocket-Client (browser)

I am trying to make a site in Vue and backend on Spring. I want to use rsocket to transfer data, but as soon as I add rsocket seurity in spring, I get :
'metadata is malformed'
Would like to take a look at a working example using jwt/simpleauth
I solved the issue with Simple Auth, now I would like to synchronize this authorization with spring websecurity.
Those. so that routing in rsocket checks authorization via websecurity. I know that this can be implemented through the jwt token, i.e. send a jwt token to a client via rest, but how can I do this in code? JS client (browser) and Spring, how do I generate userdetails token?
Just in case, I'll leave an example of the simpleauth implementation:
// METADATA BUILDER
import {encodeRoute, encodeBearerAuthMetadata, encodeSimpleAuthMetadata, encodeAndAddCustomMetadata, encodeAndAddWellKnownMetadata, MESSAGE_RSOCKET_ROUTING, MESSAGE_RSOCKET_AUTHENTICATION} from "rsocket-core";
export default class Metadata {
constructor(json) {
this.route = json['route'];
this.auth = json['auth'];
}
toMetadata() {
let metadata = Buffer.alloc(0);
if (this.auth) {
if (this.auth["type"] === 'bearer') {
metadata = encodeAndAddCustomMetadata(
metadata,
MESSAGE_RSOCKET_AUTHENTICATION.string,
encodeBearerAuthMetadata(this.auth["token"]),
);
}
if (this.auth["type"] === 'simple') {
metadata = encodeAndAddCustomMetadata(
metadata,
MESSAGE_RSOCKET_AUTHENTICATION.string,
encodeSimpleAuthMetadata(this.auth["username"], this.auth["password"]),
);
}
}
if (this.route) {
metadata = encodeAndAddWellKnownMetadata(
metadata,
MESSAGE_RSOCKET_ROUTING,
encodeRoute(this.route)
);
}
return metadata;
}
}
// RSOCKET CLIENT CLASS
import RSocketWebSocketClient from "rsocket-websocket-client";
import {BufferEncoders, MESSAGE_RSOCKET_COMPOSITE_METADATA, RSocketClient,toBuffer} from "rsocket-core";
import Metadata from "./metadata";
export default class SpringClient {
constructor(wsUrl, keepAlive = 60000, lifetime = 180000, dataMimeType = "application/json") {
this.client = new RSocketClient({
"setup": {
"keepAlive": keepAlive,
"lifetime": lifetime,
"dataMimeType": dataMimeType,
"metadataMimeType": MESSAGE_RSOCKET_COMPOSITE_METADATA.string
},
"transport": new RSocketWebSocketClient({
"url": wsUrl
}, BufferEncoders)
});
}
bearerAuth(token) {
this.auth = {type: "bearer", token: token}
}
simpleAuth(username, password) {
this.auth = {type: "simple", username: username, password: password}
}
logout() {
this.auth = null;
}
connect(
completeCallback = (socket) => {
}, errorCallback = (error) => {
}, subscribeCallback = (cancel) => {
}
) {
this.client.connect().subscribe({
onComplete: socket => {
this.socket = socket;
completeCallback(socket);
},
onError: error => {
errorCallback(error);
},
onSubscribe: cancel => {
subscribeCallback(cancel);
}
});
}
requestResponse(data, route,
completeCallback = (data) => {
},
errorCallback = (error) => {
},
subscribeCallback = (cancel) => {
}
) {
if (this.socket) {
const metadata = new Metadata({
route: route,
auth: this.auth
}).toMetadata();
data = toBuffer(data);
this.socket.requestResponse({
data,
metadata
}).subscribe({
onComplete: data => {
completeCallback(data);
},
onError: error => {
errorCallback(error);
},
onSubscribe: cancel => {
subscribeCallback(cancel);
}
});
}
}
}
// EXAMPLE, HOW TO USE
import SpringClient from "./springclient";
this.client = new SpringClient("ws://localhost:7000/", 5000, 15000, "text/plain");
this.client.connect(
(socket) => {
console.log("got connection complete");
this.socket = socket;
},
(error) => {
console.log("got connection error");
console.error(error);
},
(cancel) => {
console.log("got connection subscribe");
/* call cancel() to abort */
}
)
this.client.simpleAuth("LOGIN", "PASSWORD");
this.client.requestResponse("MESSAGE", "ROUTE",
(data) => {
console.log("got response with requestResponse");
console.log(data.data);
},
(error) => {
console.log("got error with requestResponse");
console.error(error);
},
(cancel) => {
console.log(message);
/* call cancel() to stop onComplete/onError */
}
);

Can't use Redux props in firebase redirect results

I'm trying to build Google Authentication in React + Redux + Firebase. And I want to create user after google authentication. But this.props.createUser() is not working after Redirects on Firebase.
TypeError: Cannot read property 'props' of undefined
app/src/components/home.js
import React, { Component } from "react";
import { connect } from "react-redux";
import firebase from "../config/firebase";
import { createUser } from "../actions";
class Home extends Component {
constructor(props) {
super(props);
this.state = {
user: null
};
}
async componentWillMount() {
console.log("this", this.props);
firebase.auth().onAuthStateChanged(user => {
this.setState({ user });
if (user) {
this.props.history.push("/user/settings");
}
});
firebase
.auth()
.getRedirectResult()
.then(function(result) {
if (result.credential) {
var token = result.credential.accessToken;
console.log("token", token);
}
var user = result.user;
// Successfully got a user but type error in below
this.props.createUser(user);
})
.catch(function(error) {
console.log("error", error);
});
}
onLogin = () => {
const provider = new firebase.auth.GoogleAuthProvider();
firebase.auth().signInWithRedirect(provider);
};
render() {
return (
<button className="btn btnPrimary" onClick={this.onLogin}>
<span>Google Signin</span>
</button>
);
}
}
function mapStateToProps({ user }) {
return { user: user };
}
export default connect(mapStateToProps, { createUser })(Home);
app/src/actions/index.js
import firebase from "../config/firebase";
export function createUser(user) {
console.log('user', user)
}
Since you declare the callback with the function keyword, this inside the function refers to the function itself and not to the class on which you declared the componentWillMount method.
The simplest solution is to use fat arrow notation, as you do elsewhere already:
firebase
.auth()
.getRedirectResult()
.then((result) => {
if (result.credential) {
var token = result.credential.accessToken;
console.log("token", token);
}
var user = result.user;
// Successfully got a user but type error in below
this.props.createUser(user);
})
Also see this answer on the cause of the problem, and other solutions: How to access the correct `this` inside a callback?
You need to use an arrow function here:
firebase
.auth()
.getRedirectResult()
.then(function(result) {
if (result.credential) {
var token = result.credential.accessToken;
console.log("token", token);
}
var user = result.user;
// Successfully got a user but type error in below
this.props.createUser(user);
})
.catch(function(error) {
console.log("error", error);
});
should be:
firebase
.auth()
.getRedirectResult()
.then((result) => {
if (result.credential) {
var token = result.credential.accessToken;
console.log("token", token);
}
var user = result.user;
// Successfully got a user but type error in below
this.props.createUser(user);
})
.catch(function(error) {
console.log("error", error);
});
The function() {} keyword is eating your this value.

How to update state without refreshing page in reactjs

I would like to update my dashboard if there are any changes from backend as notification in Facebook.
I have two pages:
A page for the user sending a request message
A page for the user profile where the user can see all the request messages
If there is a new request message, the user needs to refresh the user profile in order to see the new message. I want the new message to be displayed without refreshing the page. Here is my code:
In a message page
state = {
team: {
message: 'Hi! I would like to join in your team! Please accept my request',
invitation_message: 'Hi! I would like to invite you to join in my team.',
email: '',
},
}
// Invite user to a team
handleInvite = event => {
event.preventDefault();
const userObject = JSON.parse(localStorage.getItem('user'));
const jwt = userObject.jwt;
const config = {
headers: { 'Authorization': `bearer ${jwt}` },
};
api
.post('/teammembers', {
team: this.state.teaminfo,
profile: responseData.data[0],
status: "invited",
message: this.state.team.invitation_message,
}, config)
.then(response => {
this.setState({
success_message: true,
})
console.log('Success', response);
})
.catch(err => {
console.log('An error occurred:', err);
});
}
In a user profile page
export class UserProfile extends React.Component {
import socketIOClient from "socket.io-client";
state = {
invited_teams:[],
endpoint: "myurl"
}
componentDidMount() {
const { endpoint } = this.state;
//Very simply connect to the socket
const socket = socketIOClient(endpoint);
socket.on('request', (data) => {
this.setState({ state: data.requests });
});
if (localStorage.getItem('userData')) {
const userObject = JSON.parse(localStorage.getItem('user'));
api
.get(`/profiles/?user=${userObject.user.id}`)
.then(responseData => {
this.setState({
invited_teams: responseData.data
})
}
}
}
Could anyone help me to solve this problem?
Use socket.IO library. You can set a listener on new request and then update the state.
socket.on('request' , (data) => {
this.setState({state: data.requests});
});

Is it possible to extend desktop notification with 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?

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