FCM / How to handle foreground & background notifications? - javascript

I use the REST API from Firebase to send notifications to all browsers. I use PHP to send the message and handle the result with the service worker.
The problem I'm facing is that each time I send a notification, I get two notifications in the browser. One corresponds to the notification content send from PHP (foreground notification) and the other one is coming from the service worker (background notification).
While I understand the need for both, I would like to show only one depending on the context (context = browser open or not).
For the sending, I'm using the following array:
$additional["url1"]="https://www.orange.be";
$additional["url2"]="https://www.proximus.be";
$additional["url3"]="https://www.base.be";
$additional = json_encode($additional);
$fields = array(
'to'=>$to,
'notification' => [
'title' => 'new title',
'body' => 'aaa' ,
'color' => "#FF33CC",
'data'=>$additional
],
"priority" => "high",
);
And in the service worker, I have this:
messaging.onBackgroundMessage((payload) => {
//console.log('Messaging:');
//console.log(messaging);
console.log('Payload:');
console.log(payload);
additional = payload.data["gcm.notification.data"];
console.log(additional)
additional = JSON.parse(additional);
console.log(additional);
const notificationTitle = payload.notification["title"]+'(bg)';
const notificationOptions = {
body: payload.notification["body"],
icon: '/firebase-logo.png'
};
self.registration.showNotification(notificationTitle,notificationOptions);
});
If I remove the last line (self.registration), I only receive the foreground notification coming from PHP.
How can I detect the context to use either the foreground or the background notification instead of both at the same time?
Thanks

Messages received in background are handled using the onBackgroundMessage and for foreground onMessage.
Background Handler
messaging.onBackgroundMessage((payload) => {
console.log('Background Message received. ', payload);
// ...
});
Foreground Handler
messaging.onMessage((payload) => {
console.log('Message received. ', payload);
// ...
});

I found a solution based on another post here. I removed the notification from the json and passed everything through variables:
$fields = array(
'to'=> $to,
'data'=> [
"title"=> "My title",
"description"=>"My description ",
"link"=>"destination link",
"badge"=>"image url",
"image"=>"image url"],
"priority" => "high",
);
And inside the service worker, I capture the data with payload.data and associate it to the notification options
messaging.onBackgroundMessage((payload) => {
console.log('Messaging / bg');
console.log(messaging);
console.log('Payload:');
console.log(payload);
additional = payload.data;
console.log(additional)
timing = Date.now();
if (typeof additional["timing"] !=="undefined")
{
timing = additional["timing"];
}
const notificationTitle =additional["title"]+'(bg)';
const notificationOptions = {
body: additional["description"],
icon: additional["image"],
image: additional["image"],
badge: additional["badge"],
eventTime: timing,
};
// DISPLAY NOTIFICATION
self.registration.showNotification(notificationTitle,notificationOptions);});

Related

Reading all the messages at once in a chat app (updating a state multiple times) React.js

I have a simple chat component, as a part of a bigger app.
After logging in, userMessages are fetched from the backend and stored in useState.
const [userMessages, setUserMessages] = useState<ChatMessage[]>([]);
const [messages, setMessages] = useState<ChatMessage[]>([]);
userMessages - all the messages addressed to the user (from other users). Based on them, unread messages are displayed.
messages - messages belonging to a given conversation (between two users) are fetched when entering a given conversation, appear in the chat window.
When a user gets a new message while not being on chat, he gets notifications about unread messages (I used socket.io).
After clicking on the blue arrow icon, the current conversation is set (based on the message property - currentConversationId) and the messages belonging to this conversation are fetched from the database.
When they appear in the chat window each received message (only the green ones) is read...
...each message.tsx component has an useEffect that sends a request to the backend to change the status of a given message from unread to read and returns this message to the frontend, then the messages are updated using useState).
# message.tsx
useEffect(() => {
!own && !read && onReadMessage?.(_id);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
# communicationContext.tsx
const onReadMessage = async (id: string | undefined) => {
const updatedMessage = await CommunicationService.readMessage(id);
if (updatedMessage) {
let notification = {
receiverId: updatedMessage.sender?._id,
text: "read your message.",
silent: true,
read: false,
type: "message",
};
handleAddNotification?.(notification);
handleSendNotification?.({
...notification,
senderId: updatedMessage.sender?._id,
senderName: updatedMessage.sender?.name,
payload: { message: updatedMessage },
});
const updatedMessages = messages.map((message) =>
message._id === updatedMessage._id ? updatedMessage : message
);
setMessages(updatedMessages);
const updatedUserMessages = userMessages.map((message) =>
message._id === updatedMessage._id ? updatedMessage : message
);
setUserMessages(updatedUserMessages);
}
}
A request containing an updated message is also sent to the sender of the message via socket.io, then useState is also fired on the sender side and they see that the message has been read.
Up to this point everything works fine, but...
the problem arises when there are several unread messages at the same time.
In the database all messages are updated but the application status shows only 1-3 latest messages as read (depending on how many there are - sometimes only the last one is updated).
I know how useState works, so I expected this result, but I'm looking for a way around it and I'm out of ideas.
I need a solution that will update the entire state of the application, not just recent changes, without having to refresh the page.
I tried useReducer but got lost because there are too many useStates in communicationContext.tsx (here is a simplified version).
I suspect your onReadMessage handler should use functional state updates to eliminate race conditions or updating from stale state. It's a trivial change. The enqueued functional state updates will correctly update from the previous state versus whatever (likely stale) state value(s) are closed over in callback scope.
const onReadMessage = async (id: string | undefined) => {
const updatedMessage = await CommunicationService.readMessage(id);
if (updatedMessage) {
const notification = {
receiverId: updatedMessage.sender?._id,
text: "read your message.",
silent: true,
read: false,
type: "message",
};
handleAddNotification?.(notification);
handleSendNotification?.({
...notification,
senderId: updatedMessage.sender?._id,
senderName: updatedMessage.sender?.name,
payload: { message: updatedMessage },
});
const mapMessage = (message) => message._id === updatedMessage._id
? updatedMessage
: message;
setMessages(messages => messages.map(mapMessage));
setUserMessages(userMessages => userMessages.map(mapMessage));
}
};

discord.js Editting Slash Command Interaction Messages

So I'm using discord.js with this code here:
client.api.interactions(interaction.id, interaction.token).callback.post({
data: {
type: 4,
data: {
content: "Getting Data..."
}
}
})
I would like to be able to edit this message afterwards, but everything I have seen requireds a message id, and I seem to be unable to get the message id off of this code.
You can edit an interaction response with this patch request (See: Followup Messages):
PATCH /webhooks/<application_id>/<interaction_token>/messages/#original
Basic example using the axios library:
axios.patch(`https://discord.com/api/v8/webhooks/${appId}/${interaction.token}/messages/#original`, { content: 'New content' });
The answer to this request will also contain the message-id.
Here is an example function that will edit the original message either as plain text or an embed object and returns the discord message object for further usage (e.g. add reply emojis etc.):
const editInteraction = async (client, interaction, response) => {
// Set the data as embed if reponse is an embed object else as content
const data = typeof response === 'object' ? { embeds: [ response ] } : { content: response };
// Get the channel object by channel id:
const channel = await client.channels.resolve(interaction.channel_id);
// Edit the original interaction response:
return axios
.patch(`https://discord.com/api/v8/webhooks/${appId}/${interaction.token}/messages/#original`, data)
.then((answer) => {
// Return the message object:
return channel.messages.fetch(answer.data.id)
})
};
Also instead of sending an initial message like "getting data..." you also can send an empty response of type 5. This is the build-in method and displays a little loading animation too :) See here (One advantage is that this way no "edited" appears.)
client.api.interactions(interaction.id, interaction.token).callback.post({
data: {
type: 5,
},
})

How to add some object to sendMessage or add this object to Extra in telegraf.js library

I have telegram bot using a telegraf.js library. I have some users that have been registered in bot, and I want to send a confirmation of something consent from one user to another. For realizing it, I need to add in sendMessage some consentId, but I don`t know how!
(this is pseudo-code, here I show my imagination of how that can be work)
bot.action('createContract', ctx => {
// we have some consent object from sequlize with right id
const consent = {
id : uuid4(),
status : 'PENDING',
initiatorId : uuid4(),
consonantId : uuid4()
};
sequelize.model.User.findByPk(consent.consonantId).then(user => {
// here i need add consentId to sendMessage but not show it to user
ctx.telegram.sendMessage(
user.chatId,
'Do you confirm ....',
Extra.HTML().markup((m) =>
m.inlineKeyboard([
m.callbackButton('sign', 'sign'),
m.callbackButton('decline', 'decline')
]))
);
});
});
bot.action('sign', ctx => {
// here we must a get consentId that we add in createContract action
sequelize.model.Consent.findByPk(ctx.consentId).then(consent => {
return consent.update({ status: 'SUCCESS' });
});
});

Raise ServiceWorker Push Notification Event

I'm developing a webApp, and want to send notifications using a ServiceWorker.
I have my sw.js and my main.js. I'm able to register my Service Worker and I'm able to send notifications using a command like:
navigator.serviceWorker.getRegistration().then(function(reg) {
reg.showNotification('Title', {
body: 'body'
});
});
But this is not calling my push event defined in the 'sw.js'.
For reference here is my "sw.js":
self.addEventListener('push', function(event) {
console.log('[Service Worker] Push Received.');
console.log(`[Service Worker] Push had this data: "${event.data.text()}"`);
const title = 'title test from push';
const options = {
body: 'body test from push.',
icon: 'images/icon.png'
};
event.waitUntil(self.registration.showNotification(title, options));
});
const title = 'Push Codelab';
const options = {
body: 'Yay it works.',
icon: 'images/icon.png',
badge: 'images/badge.png'
};
self.registration.showNotification(title, options);
As additional information, in Chrome if I go in the Developer tool => Application Tab => Service Workers, and click on "push" button, the correct push event is raised. But, again, I'm not able to trigger it from my main page.

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