Files
musicland/src/providers/NotificationProvider.js
T
Thomas Demirdjian 4bb083ea46 start adding payment
2025-11-04 14:46:12 +01:00

268 lines
6.5 KiB
JavaScript

import Constants from "expo-constants";
import * as Device from "expo-device";
import * as Notifications from "expo-notifications";
import React, {
createContext,
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import { AppState, Platform } from "react-native";
import { useGlobal } from "reactn";
import firebase, {
arrayUnion,
notificationsRef,
serverTimestamp,
usersRef,
} from "../config/firebase";
import useDataFromRef from "../hooks/useDataFromRef";
export const NotificationContext = createContext(null);
export default function NotificationProvider({ children }) {
const [user] = useGlobal("currentUserData");
const [uid] = useGlobal("currentUID");
const [pendingData, setPendingData] = useGlobal("pendingData");
const [notifInit, setNotifInit] = useState(false);
const [allowNotifications, setAllowNotifications] = useState(false);
const notificationListener = useRef();
const responseListener = useRef();
const notificationsQuery = useMemo(() => {
if (!uid) {
return null;
}
try {
return notificationsRef
.where("receiver", "==", uid)
.orderBy("time", "desc");
} catch (error) {
console.log("notificationsQuery error:", error);
return null;
}
}, [uid]);
const {
data: notifications = [],
loading: loadingNotifications,
setData: setNotifications,
} = useDataFromRef({
ref: notificationsQuery,
initialState: [],
refreshArray: [uid],
listener: true,
condition: !!notificationsQuery,
});
const unreadCount = useMemo(() => {
try {
return notifications.filter((notif) => !notif?.read).length;
} catch (_error) {
return 0;
}
}, [notifications]);
const clearBadgeCount = useCallback(async () => {
if (Platform.OS === "web") {
return;
}
try {
await Notifications.setBadgeCountAsync(0);
} catch (error) {
console.log("clearBadgeCount error:", error);
}
}, []);
const markNotificationAsRead = useCallback(async (notificationId) => {
try {
if (!notificationId) return;
await notificationsRef.doc(notificationId).set(
{
read: true,
readAt: serverTimestamp(),
},
{ merge: true }
);
} catch (error) {
console.log("markNotificationAsRead error:", error);
}
}, []);
const markAllNotificationsAsRead = useCallback(async () => {
try {
if (!notifications?.length) {
return;
}
const unread = notifications.filter((notif) => !notif?.read);
if (!unread.length) {
return;
}
const batch = firebase.firestore().batch();
unread.forEach((notif) => {
if (!notif?.id) return;
batch.set(
notificationsRef.doc(notif.id),
{
read: true,
readAt: serverTimestamp(),
},
{ merge: true }
);
});
await batch.commit();
} catch (error) {
console.log("markAllNotificationsAsRead error:", error);
}
}, [notifications]);
useEffect(() => {
if (!uid || !user || notifInit) {
return;
}
registerForPushNotificationsAsync();
}, [user, uid, notifInit]);
useEffect(() => {
if (Platform.OS === "web") {
return;
}
clearBadgeCount();
const handleAppStateChange = (state) => {
if (state === "active") {
clearBadgeCount();
}
};
const subscription = AppState.addEventListener(
"change",
handleAppStateChange
);
return () => {
if (subscription?.remove) {
subscription.remove();
} else {
AppState.removeEventListener("change", handleAppStateChange);
}
};
}, [clearBadgeCount]);
useEffect(() => {
if (!allowNotifications) {
return;
}
notificationListener.current =
Notifications.addNotificationReceivedListener(async (notification) => {
console.log("Notification received", notification);
await Notifications.scheduleNotificationAsync(notification?.request);
});
responseListener.current =
Notifications.addNotificationResponseReceivedListener(
async (response) => {
console.log("Notification response", response);
if (
response?.actionIdentifier ===
"expo.modules.notifications.actions.DEFAULT"
) {
const { data } = response.notification.request.content;
if (data) {
console.log("Notification data", data);
await setPendingData(data);
}
}
}
);
return () => {
Notifications.removeNotificationSubscription(
notificationListener.current
);
Notifications.removeNotificationSubscription(responseListener.current);
};
}, [allowNotifications, setPendingData]);
async function registerForPushNotificationsAsync() {
try {
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);
} finally {
setNotifInit(true);
}
}
return (
<NotificationContext.Provider
value={{
allowNotifications,
pendingData,
setPendingData,
notifications,
setNotifications,
loadingNotifications,
unreadCount,
markNotificationAsRead,
markAllNotificationsAsRead,
}}
>
{children}
</NotificationContext.Provider>
);
}