Am working on a certain project. So i want to test out Expo notifications. I have created my notifications.ts to handle the registering and sending notifications. And am also having a notification provider.
I have a button, which when clicked should call the send notification function and display the notification.
To my wonder, if i send a notification using expo notification tool, the notification is displayed but if i click the button in the app. A console log shows that the notfication has been sent but it doesnt get displayed. Any help rendered is really appreciated.
Notifications.ts
import { Platform } from "react-native";
import * as Notifications from "expo-notifications";
import Constants from "expo-constants";
import * as Device from "expo-device";
function handleRegistrationError(errorMessage: string) {
return null;
}
export async function registerForPushNotificationsAsync() {
if (Platform.OS === "android") {
Notifications.setNotificationChannelAsync("default", {
name: "default",
importance: Notifications.AndroidImportance.MAX,
vibrationPattern: [0, 250, 250, 250],
lightColor: "#FF231F7C",
});
}
if (Device.isDevice) {
const { status: existingStatus } =
await Notifications.getPermissionsAsync();
let finalStatus = existingStatus;
if (existingStatus !== "granted") {
const { status } = await Notifications.requestPermissionsAsync();
finalStatus = status;
}
if (finalStatus !== "granted") {
handleRegistrationError(
"Permission not granted to get push token for push notification!"
);
return;
}
const projectId =
Constants?.expoConfig?.extra?.eas?.projectId ??
Constants?.easConfig?.projectId;
if (!projectId) {
handleRegistrationError("Project ID not found");
}
try {
const pushTokenString = (
await Notifications.getExpoPushTokenAsync({
projectId,
})
).data;
console.log("My push toke string", pushTokenString);
return pushTokenString;
} catch (e: unknown) {
handleRegistrationError(`${e}`);
}
} else {
handleRegistrationError("Must use physical device for push notifications");
}
}
export async function sendPushNotification(expoPushToken: string) {
const message = {
to: expoPushToken,
sound: "default",
title: "Original Title",
body: "And here is the body!",
data: { someData: "goes here" },
};
await fetch("https://exp.host/--/api/v2/push/send", {
method: "POST",
headers: {
Accept: "application/json",
"Accept-encoding": "gzip, deflate",
"Content-Type": "application/json",
},
body: JSON.stringify(message),
});
}
Below is the NotificationProvider
import React, { PropsWithChildren, useEffect, useState, useRef } from "react";
import {
registerForPushNotificationsAsync,
sendPushNotification,
} from "@/src/lib/notifications";
import { ExpoPushToken } from "expo-notifications";
import * as Notifications from "expo-notifications";
import { useAuth } from "./AuthProvider";
import { supabase } from "../lib/supabase";
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: true,
shouldSetBadge: false,
}),
});
const NotificationProvider = ({ children }: PropsWithChildren) => {
const [expoPushToken, setExpoPushToken] = useState<ExpoPushToken | string>();
const [notification, setNotification] = useState<
Notifications.Notification | undefined
>(undefined);
const { profile } = useAuth();
const notificationListener = useRef<Notifications.Subscription>();
const responseListener = useRef<Notifications.Subscription>();
const savePushToken = async (newToken: string) => {
setExpoPushToken(newToken ?? "");
if (!newToken) {
return;
}
// Save the new token to your database
await supabase
.from("profiles")
.update({ expo_push_token: newToken })
.eq("id", profile.id);
};
useEffect(() => {
registerForPushNotificationsAsync()
.then((token) => savePushToken(token))
.catch((error: any) => setExpoPushToken(`${error}`));
notificationListener.current =
Notifications.addNotificationReceivedListener((notification) => {
setNotification(notification);
});
responseListener.current =
Notifications.addNotificationResponseReceivedListener((response) => {
console.log(response);
});
return () => {
notificationListener.current &&
Notifications.removeNotificationSubscription(
notificationListener.current
);
responseListener.current &&
Notifications.removeNotificationSubscription(responseListener.current);
};
}, []);
const sendTestNotification = async () => {
if (expoPushToken) {
try {
await sendPushNotification(expoPushToken as string);
console.log("Test notification sent");
} catch (error) {
console.error("Error sending test notification:", error);
}
} else {
console.warn("Expo push token not available");
}
};
// Expose the sendTestNotification function to child components
const value = {
sendTestNotification,
};
return (
<NotificationContext.Provider value={value}>
{children}
</NotificationContext.Provider>
);
};
export default NotificationProvider;
// Create a context to expose the sendTestNotification function
export const NotificationContext = React.createContext<{
sendTestNotification: () => Promise<void>;
} | null>(null);
// Create a custom hook to use the notification context
export const useNotification = () => {
const context = React.useContext(NotificationContext);
if (!context) {
throw new Error(
"useNotification must be used within a NotificationProvider"
);
}
return context;
};
import { View, Button } from "react-native";
import { useNotification } from "@/src/providers/NotificationProvider";
const HomeScreen = () => {
const { sendTestNotification } = useNotification();
return (
<View>
{/* Your other components */}
<Button title="Test Notification" onPress={sendTestNotification} />
</View>
);
};
export default HomeScreen;