268 lines
6.5 KiB
JavaScript
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>
|
|
);
|
|
}
|