web notifications
This commit is contained in:
+129
-31
@@ -19,23 +19,34 @@ exports.sendNotificationWhenDocIsCreated = onDocumentCreated(
|
||||
data: notifData = {},
|
||||
} = event.data.data();
|
||||
|
||||
const { pushToken = null } =
|
||||
(await db.collection(receiverCollection).doc(receiver).get())?.data() ||
|
||||
{};
|
||||
|
||||
if (!receiver || !message) {
|
||||
throw new Error("Receiver and message are required");
|
||||
}
|
||||
|
||||
if (pushToken) {
|
||||
await sendExpoNotification(pushToken, {
|
||||
title: title || "MusicLand",
|
||||
message: message,
|
||||
data: notifData || {},
|
||||
});
|
||||
} else {
|
||||
const userSnap = await db.collection(receiverCollection).doc(receiver).get();
|
||||
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",
|
||||
message: message,
|
||||
data: notifData || {},
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return e;
|
||||
@@ -44,29 +55,83 @@ exports.sendNotificationWhenDocIsCreated = onDocumentCreated(
|
||||
);
|
||||
|
||||
// Fonction pour envoyer la notification via Expo SDK
|
||||
async function sendExpoNotification(
|
||||
pushToken,
|
||||
{ title = "", message = "", data = {} }
|
||||
) {
|
||||
async function sendExpoNotification({
|
||||
tokens = [],
|
||||
title = "",
|
||||
message = "",
|
||||
data = {},
|
||||
receiverId = null,
|
||||
receiverCollection = "users",
|
||||
}) {
|
||||
try {
|
||||
if (!Expo.isExpoPushToken(pushToken)) {
|
||||
throw new Error(`Invalid push token: ${pushToken}`);
|
||||
} else {
|
||||
console.log("Valid push token:", pushToken);
|
||||
const candidateTokens = Array.isArray(tokens) ? tokens : [tokens];
|
||||
const validTokens = [];
|
||||
const invalidTokens = [];
|
||||
|
||||
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,
|
||||
});
|
||||
}
|
||||
|
||||
if (!validTokens.length) {
|
||||
console.warn("No valid Expo push tokens to send notification");
|
||||
return { sent: false };
|
||||
}
|
||||
|
||||
const messages = validTokens.map((token) => ({
|
||||
to: token,
|
||||
sound: "default",
|
||||
title: title,
|
||||
body: message,
|
||||
data: data || {},
|
||||
priority: "high",
|
||||
badge: 1,
|
||||
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,
|
||||
});
|
||||
}
|
||||
|
||||
const receipts = await expo.sendPushNotificationsAsync([
|
||||
{
|
||||
to: pushToken,
|
||||
sound: "default",
|
||||
title: title,
|
||||
body: message,
|
||||
data: data || {},
|
||||
priority: "high",
|
||||
badge: 1,
|
||||
channelId: "default",
|
||||
},
|
||||
]);
|
||||
console.log("Sent push notifications:", receipts);
|
||||
|
||||
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
|
||||
const sendNotification = async ({
|
||||
sender = "SYSTEM",
|
||||
|
||||
Reference in New Issue
Block a user