web notifications
This commit is contained in:
+120
-22
@@ -19,23 +19,34 @@ exports.sendNotificationWhenDocIsCreated = onDocumentCreated(
|
|||||||
data: notifData = {},
|
data: notifData = {},
|
||||||
} = event.data.data();
|
} = event.data.data();
|
||||||
|
|
||||||
const { pushToken = null } =
|
|
||||||
(await db.collection(receiverCollection).doc(receiver).get())?.data() ||
|
|
||||||
{};
|
|
||||||
|
|
||||||
if (!receiver || !message) {
|
if (!receiver || !message) {
|
||||||
throw new Error("Receiver and message are required");
|
throw new Error("Receiver and message are required");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pushToken) {
|
const userSnap = await db.collection(receiverCollection).doc(receiver).get();
|
||||||
await sendExpoNotification(pushToken, {
|
const { pushToken = null, pushTokens = [] } = userSnap?.data() || {};
|
||||||
|
|
||||||
|
const tokensSet = new Set(
|
||||||
|
[]
|
||||||
|
.concat(Array.isArray(pushTokens) ? pushTokens : [])
|
||||||
|
.concat(pushToken ? [pushToken] : [])
|
||||||
|
.filter(Boolean)
|
||||||
|
);
|
||||||
|
const tokens = Array.from(tokensSet);
|
||||||
|
|
||||||
|
if (!tokens?.length) {
|
||||||
|
console.warn("User push token not found");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
await sendExpoNotification({
|
||||||
|
tokens,
|
||||||
|
receiverId: receiver,
|
||||||
|
receiverCollection,
|
||||||
title: title || "MusicLand",
|
title: title || "MusicLand",
|
||||||
message: message,
|
message: message,
|
||||||
data: notifData || {},
|
data: notifData || {},
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
console.warn("User push token not found");
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
return e;
|
return e;
|
||||||
@@ -44,20 +55,42 @@ exports.sendNotificationWhenDocIsCreated = onDocumentCreated(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Fonction pour envoyer la notification via Expo SDK
|
// Fonction pour envoyer la notification via Expo SDK
|
||||||
async function sendExpoNotification(
|
async function sendExpoNotification({
|
||||||
pushToken,
|
tokens = [],
|
||||||
{ title = "", message = "", data = {} }
|
title = "",
|
||||||
) {
|
message = "",
|
||||||
|
data = {},
|
||||||
|
receiverId = null,
|
||||||
|
receiverCollection = "users",
|
||||||
|
}) {
|
||||||
try {
|
try {
|
||||||
if (!Expo.isExpoPushToken(pushToken)) {
|
const candidateTokens = Array.isArray(tokens) ? tokens : [tokens];
|
||||||
throw new Error(`Invalid push token: ${pushToken}`);
|
const validTokens = [];
|
||||||
} else {
|
const invalidTokens = [];
|
||||||
console.log("Valid push token:", pushToken);
|
|
||||||
|
candidateTokens.forEach((token) => {
|
||||||
|
if (Expo.isExpoPushToken(token)) {
|
||||||
|
validTokens.push(token);
|
||||||
|
} else if (token) {
|
||||||
|
invalidTokens.push(token);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (invalidTokens.length && receiverId) {
|
||||||
|
await removeInvalidTokens({
|
||||||
|
tokens: invalidTokens,
|
||||||
|
receiverId,
|
||||||
|
receiverCollection,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const receipts = await expo.sendPushNotificationsAsync([
|
if (!validTokens.length) {
|
||||||
{
|
console.warn("No valid Expo push tokens to send notification");
|
||||||
to: pushToken,
|
return { sent: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
const messages = validTokens.map((token) => ({
|
||||||
|
to: token,
|
||||||
sound: "default",
|
sound: "default",
|
||||||
title: title,
|
title: title,
|
||||||
body: message,
|
body: message,
|
||||||
@@ -65,8 +98,40 @@ async function sendExpoNotification(
|
|||||||
priority: "high",
|
priority: "high",
|
||||||
badge: 1,
|
badge: 1,
|
||||||
channelId: "default",
|
channelId: "default",
|
||||||
},
|
}));
|
||||||
]);
|
|
||||||
|
const chunks = expo.chunkPushNotifications(messages);
|
||||||
|
const receipts = [];
|
||||||
|
const tokensToPrune = new Set();
|
||||||
|
|
||||||
|
for (const chunk of chunks) {
|
||||||
|
const chunkReceipts = await expo.sendPushNotificationsAsync(chunk);
|
||||||
|
chunkReceipts.forEach((receipt, index) => {
|
||||||
|
if (receipt?.status === "error") {
|
||||||
|
const errorCode = receipt?.details?.error || receipt?.details?.code;
|
||||||
|
console.log("Error sending notification:", receipt);
|
||||||
|
if (
|
||||||
|
errorCode === "DeviceNotRegistered" ||
|
||||||
|
errorCode === "PushTokenNotRegistered"
|
||||||
|
) {
|
||||||
|
const token = chunk[index]?.to;
|
||||||
|
if (token) {
|
||||||
|
tokensToPrune.add(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
receipts.push(...chunkReceipts);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokensToPrune.size && receiverId) {
|
||||||
|
await removeInvalidTokens({
|
||||||
|
tokens: Array.from(tokensToPrune),
|
||||||
|
receiverId,
|
||||||
|
receiverCollection,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
console.log("Sent push notifications:", receipts);
|
console.log("Sent push notifications:", receipts);
|
||||||
|
|
||||||
return { sent: true };
|
return { sent: true };
|
||||||
@@ -76,6 +141,39 @@ async function sendExpoNotification(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function removeInvalidTokens({ tokens = [], receiverId, receiverCollection }) {
|
||||||
|
try {
|
||||||
|
if (!receiverId || !tokens.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uniqueTokens = Array.from(new Set(tokens.filter(Boolean)));
|
||||||
|
if (!uniqueTokens.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const docRef = db.collection(receiverCollection).doc(receiverId);
|
||||||
|
const userSnap = await docRef.get();
|
||||||
|
const userData = userSnap?.data() || {};
|
||||||
|
|
||||||
|
const updates = {
|
||||||
|
pushTokens: admin.firestore.FieldValue.arrayRemove(...uniqueTokens),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (uniqueTokens.includes(userData?.pushToken)) {
|
||||||
|
updates.pushToken = admin.firestore.FieldValue.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
await docRef.set(updates, { merge: true });
|
||||||
|
console.log(
|
||||||
|
"Pruned invalid push tokens",
|
||||||
|
JSON.stringify({ receiverId, tokens: uniqueTokens }, null, 2)
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Failed to prune invalid push tokens:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Fonction pour ajouter une notification à la base de données
|
// Fonction pour ajouter une notification à la base de données
|
||||||
const sendNotification = async ({
|
const sendNotification = async ({
|
||||||
sender = "SYSTEM",
|
sender = "SYSTEM",
|
||||||
|
|||||||
@@ -9,8 +9,10 @@ import React, {
|
|||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from "react";
|
} from "react";
|
||||||
|
import { Platform } from "react-native";
|
||||||
import { useGlobal } from "reactn";
|
import { useGlobal } from "reactn";
|
||||||
import firebase, {
|
import firebase, {
|
||||||
|
arrayUnion,
|
||||||
notificationsRef,
|
notificationsRef,
|
||||||
serverTimestamp,
|
serverTimestamp,
|
||||||
usersRef,
|
usersRef,
|
||||||
@@ -152,35 +154,52 @@ export default function NotificationProvider({ children }) {
|
|||||||
|
|
||||||
async function registerForPushNotificationsAsync() {
|
async function registerForPushNotificationsAsync() {
|
||||||
try {
|
try {
|
||||||
if (Device.isDevice) {
|
const isWeb = Platform.OS === "web";
|
||||||
|
if (!Device.isDevice && !isWeb) {
|
||||||
|
console.log("Must use physical device for Push Notifications");
|
||||||
|
setAllowNotifications(false);
|
||||||
|
setNotifInit(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let { status } = await Notifications.getPermissionsAsync();
|
let { status } = await Notifications.getPermissionsAsync();
|
||||||
if (status !== "granted") {
|
if (status !== "granted") {
|
||||||
const request = await Notifications.requestPermissionsAsync();
|
const request = await Notifications.requestPermissionsAsync();
|
||||||
status = request.status;
|
status = request.status;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status !== "granted") {
|
if (status !== "granted") {
|
||||||
console.log("Notification permissions denied");
|
console.log("Notification permissions denied");
|
||||||
setAllowNotifications(false);
|
setAllowNotifications(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setAllowNotifications(true);
|
setAllowNotifications(true);
|
||||||
|
|
||||||
// Get the push token
|
const projectId =
|
||||||
|
Constants.expoConfig?.extra?.eas?.projectId ||
|
||||||
|
Constants.manifest2?.extra?.eas?.projectId ||
|
||||||
|
Constants.manifest?.extra?.eas?.projectId ||
|
||||||
|
"";
|
||||||
|
|
||||||
const pushToken = (
|
const pushToken = (
|
||||||
await Notifications.getExpoPushTokenAsync({
|
await Notifications.getExpoPushTokenAsync({
|
||||||
projectId: Constants.expoConfig?.extra?.eas?.projectId || "",
|
projectId,
|
||||||
})
|
})
|
||||||
).data;
|
)?.data;
|
||||||
if (user?.pushToken !== pushToken && !!user) {
|
|
||||||
await usersRef.doc(uid).update({ pushToken });
|
if (!pushToken) {
|
||||||
// if (!user?.alerts) {
|
console.log("No push token retrieved");
|
||||||
// await usersRef.doc(uid).update({
|
return;
|
||||||
// alerts: Object.values(ALERT_TYPE),
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
console.log("Must use physical device for Push Notifications");
|
if (!!user && pushToken) {
|
||||||
|
await usersRef.doc(uid).set(
|
||||||
|
{
|
||||||
|
pushTokens: arrayUnion(pushToken),
|
||||||
|
},
|
||||||
|
{ merge: true }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
|
|||||||
Reference in New Issue
Block a user