Untitled
unknown
plain_text
7 months ago
7.4 kB
5
Indexable
import messaging from '@react-native-firebase/messaging';
import { Platform } from 'react-native';
import * as Device from 'expo-device';
export class FirebaseMessagingService {
private static instance: FirebaseMessagingService;
private _fcmToken: string | null = null;
private constructor() {}
static getInstance(): FirebaseMessagingService {
if (!FirebaseMessagingService.instance) {
FirebaseMessagingService.instance = new FirebaseMessagingService();
}
return FirebaseMessagingService.instance;
}
/**
* Request permission to receive push notifications
*/
async requestPermission(): Promise<boolean> {
if (!Device.isDevice) {
console.log('Must use physical device for Push Notifications');
return false;
}
try {
const authStatus = await messaging().requestPermission();
const enabled =
authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
authStatus === messaging.AuthorizationStatus.PROVISIONAL;
if (enabled) {
console.log('Firebase messaging authorization status:', authStatus);
return true;
} else {
console.log('Firebase messaging authorization denied');
return false;
}
} catch (error) {
console.error('Error requesting notification permission:', error);
return false;
}
}
/**
* Get the FCM token for this device
*/
async getFCMToken(): Promise<string | null> {
try {
// Always clear the cached token to force a fresh attempt
this._fcmToken = null;
console.log('Platform:', Platform.OS);
// For iOS, we need to register for remote messages before getting the token
if (Platform.OS === 'ios') {
try {
// Force iOS to use the sandbox APNs environment for development builds
if (__DEV__) {
console.log('Setting up Firebase Messaging for development (sandbox APNs)');
// This is a workaround to force Firebase to use the sandbox APNs environment
// It's not officially documented but might help in some cases
const messagingInstance = messaging();
(messagingInstance as any)._useSandbox = true;
}
console.log('iOS: Checking if already registered for remote messages');
const isRegistered = await messaging().isDeviceRegisteredForRemoteMessages;
console.log('iOS: Device registered status:', isRegistered);
// Always force re-registration to ensure we have the latest registration
console.log('iOS: Registering device for remote messages');
await messaging().registerDeviceForRemoteMessages();
console.log('iOS: Device successfully registered for remote messages');
// Wait for APNs token to be available
console.log('iOS: Checking for APNs token...');
const apnsToken = await messaging().getAPNSToken();
console.log('iOS: APNs token:', apnsToken || 'Not available');
if (!apnsToken) {
console.warn('iOS: No APNs token available');
}
} catch (registerError) {
console.error('Error registering for remote messages:', registerError);
if (registerError instanceof Error) {
console.error('Error message:', registerError.message);
console.error('Error stack:', registerError.stack);
}
throw registerError;
}
}
console.log('Getting FCM token...');
// Get the token
const token = await messaging().getToken();
console.log('FCM Token received:', token ? 'Yes (length: ' + token.length + ')' : 'No');
this._fcmToken = token;
return token;
} catch (error) {
console.error('Error getting FCM token:', error);
if (error instanceof Error) {
console.error('Error message:', error.message);
console.error('Error stack:', error.stack);
}
return null;
}
}
/**
* Register for foreground notifications
* @param callback Function to call when a notification is received in foreground
*/
onForegroundMessage(callback: (message: any) => void) {
console.log('Setting up Firebase foreground message handler');
return messaging().onMessage((message) => {
console.log('Firebase message received in foreground:', message);
callback(message);
});
}
/**
* Register for background notifications
* This must be called outside of any component
* @param callback Function to call when a notification is received in background
*/
static setBackgroundMessageHandler(callback: (message: any) => Promise<void>) {
console.log('Setting up Firebase background message handler');
messaging().setBackgroundMessageHandler(async (message) => {
console.log('Firebase message received in background:', message);
return callback(message);
});
}
/**
* Register for notification open events
* @param callback Function to call when a notification is opened
*/
onNotificationOpen(callback: (message: any) => void) {
return messaging().onNotificationOpenedApp(callback);
}
/**
* Check if the app was opened from a notification
*/
async getInitialNotification() {
return await messaging().getInitialNotification();
}
/**
* Subscribe to a topic
* @param topic Topic to subscribe to
*/
async subscribeToTopic(topic: string): Promise<void> {
try {
await messaging().subscribeToTopic(topic);
console.log(`Subscribed to topic: ${topic}`);
} catch (error) {
console.error(`Error subscribing to topic ${topic}:`, error);
}
}
/**
* Unsubscribe from a topic
* @param topic Topic to unsubscribe from
*/
async unsubscribeFromTopic(topic: string): Promise<void> {
try {
await messaging().unsubscribeFromTopic(topic);
console.log(`Unsubscribed from topic: ${topic}`);
} catch (error) {
console.error(`Error unsubscribing from topic ${topic}:`, error);
}
}
/**
* Configure the notification channel for Android
* This is required for Android 8.0+
*/
async createAndroidChannel(): Promise<void> {
if (Platform.OS === 'android') {
try {
// For Firebase, we'll use the Expo Notifications API to create a channel
// that will be used by Firebase notifications
const { Notifications } = require('expo-notifications');
await Notifications.setNotificationChannelAsync('firebase-messaging-channel', {
name: 'Firebase Notifications',
description: 'Channel for Firebase Cloud Messaging',
importance: Notifications.AndroidImportance.MAX,
vibrationPattern: [0, 250, 250, 250],
lightColor: '#FF231F7C',
sound: 'default',
});
console.log('Android notification channel setup complete for Firebase');
} catch (error) {
console.error('Error creating Android notification channel:', error);
}
}
}
get fcmToken(): string | null {
return this._fcmToken;
}
}
// Set up background message handler
FirebaseMessagingService.setBackgroundMessageHandler(async (remoteMessage) => {
console.log('Message handled in the background!', remoteMessage);
});
export default FirebaseMessagingService.getInstance();
Editor is loading...
Leave a Comment