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",
|
||||
|
||||
@@ -9,8 +9,10 @@ import React, {
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { Platform } from "react-native";
|
||||
import { useGlobal } from "reactn";
|
||||
import firebase, {
|
||||
arrayUnion,
|
||||
notificationsRef,
|
||||
serverTimestamp,
|
||||
usersRef,
|
||||
@@ -152,35 +154,52 @@ export default function NotificationProvider({ children }) {
|
||||
|
||||
async function registerForPushNotificationsAsync() {
|
||||
try {
|
||||
if (Device.isDevice) {
|
||||
let { status } = await Notifications.getPermissionsAsync();
|
||||
if (status !== "granted") {
|
||||
const request = await Notifications.requestPermissionsAsync();
|
||||
status = request.status;
|
||||
}
|
||||
if (status !== "granted") {
|
||||
console.log("Notification permissions denied");
|
||||
setAllowNotifications(false);
|
||||
return;
|
||||
}
|
||||
setAllowNotifications(true);
|
||||
|
||||
// Get the push token
|
||||
const pushToken = (
|
||||
await Notifications.getExpoPushTokenAsync({
|
||||
projectId: Constants.expoConfig?.extra?.eas?.projectId || "",
|
||||
})
|
||||
).data;
|
||||
if (user?.pushToken !== pushToken && !!user) {
|
||||
await usersRef.doc(uid).update({ pushToken });
|
||||
// if (!user?.alerts) {
|
||||
// await usersRef.doc(uid).update({
|
||||
// alerts: Object.values(ALERT_TYPE),
|
||||
// });
|
||||
// }
|
||||
}
|
||||
} else {
|
||||
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();
|
||||
if (status !== "granted") {
|
||||
const request = await Notifications.requestPermissionsAsync();
|
||||
status = request.status;
|
||||
}
|
||||
|
||||
if (status !== "granted") {
|
||||
console.log("Notification permissions denied");
|
||||
setAllowNotifications(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setAllowNotifications(true);
|
||||
|
||||
const projectId =
|
||||
Constants.expoConfig?.extra?.eas?.projectId ||
|
||||
Constants.manifest2?.extra?.eas?.projectId ||
|
||||
Constants.manifest?.extra?.eas?.projectId ||
|
||||
"";
|
||||
|
||||
const pushToken = (
|
||||
await Notifications.getExpoPushTokenAsync({
|
||||
projectId,
|
||||
})
|
||||
)?.data;
|
||||
|
||||
if (!pushToken) {
|
||||
console.log("No push token retrieved");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!!user && pushToken) {
|
||||
await usersRef.doc(uid).set(
|
||||
{
|
||||
pushTokens: arrayUnion(pushToken),
|
||||
},
|
||||
{ merge: true }
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
|
||||
Reference in New Issue
Block a user