Files
FarmaFinder/apps/frontend-mobile/services/notifications.ts
Antoni Nuñez Romeu 0c43d32cf1
Run Tests on Branches / Detect Changes (push) Successful in 13s
Run Tests on Branches / Backend Tests (push) Successful in 1m58s
Run Tests on Branches / Frontend Tests (push) Successful in 1m53s
Run Tests on Branches / Frontend Mobile Tests (push) Successful in 1m49s
feat: add Expo Push notifications for mobile and configure EXPO_ACCESS_TOKEN
- Backend: add expo_push_tokens table, expo-register/unregister endpoints, hybrid VAPID+Expo push sender
- Mobile: register Expo push token on app launch, bell toggle on medicine/pharmacy detail screens
- .env.example: document EXPO_ACCESS_TOKEN configuration
- Dark mode hover fixes for search suggestions and recent buttons
2026-07-13 12:21:25 +02:00

137 lines
3.6 KiB
TypeScript

import * as Notifications from 'expo-notifications';
import * as Device from 'expo-device';
import * as Constants from 'expo-constants';
import { Platform } from 'react-native';
import api from './api';
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowBanner: true,
shouldShowList: true,
shouldPlaySound: true,
shouldSetBadge: true,
}),
});
let _cachedToken: string | null = null;
export async function registerForPushNotifications() {
if (!Device.isDevice) {
console.log('Push notifications require a physical device');
return null;
}
const { status: existingStatus } = await Notifications.getPermissionsAsync();
let finalStatus = existingStatus;
if (existingStatus !== 'granted') {
const { status } = await Notifications.requestPermissionsAsync();
finalStatus = status;
}
if (finalStatus !== 'granted') {
console.log('Failed to get push token for push notification');
return null;
}
if (Platform.OS === 'android') {
await Notifications.setNotificationChannelAsync('default', {
name: 'default',
importance: Notifications.AndroidImportance.MAX,
vibrationPattern: [0, 250, 250, 250],
lightColor: '#007AFF',
});
}
try {
const projectId =
Constants?.expoConfig?.extra?.eas?.projectId ?? Constants?.easConfig?.projectId;
const token = await Notifications.getExpoPushTokenAsync({
projectId,
});
_cachedToken = token.data;
// Register token with backend (fire and forget)
registerTokenWithBackend(token.data).catch(() => {});
return token.data;
} catch (e) {
console.log('Error getting push token:', e);
return null;
}
}
async function registerTokenWithBackend(token: string) {
try {
await api.post('/notifications/expo-register', { token });
} catch (e) {
console.log('Failed to register push token with backend:', e);
}
}
export async function subscribeToMedicine(
medicineNregistro: string,
medicineName: string | null,
pharmacyId?: number | null
) {
const token = _cachedToken;
if (!token) {
console.log('No push token available for subscription');
return;
}
try {
await api.post('/notifications/expo-register', {
token,
medicine_nregistro: medicineNregistro,
medicine_name: medicineName,
pharmacy_id: pharmacyId || null,
});
} catch (e) {
console.log('Failed to subscribe to medicine notifications:', e);
}
}
export async function unsubscribeFromMedicine(
medicineNregistro: string,
pharmacyId?: number | null
) {
try {
await api.delete('/notifications/expo-unregister', {
data: {
medicine_nregistro: medicineNregistro,
pharmacy_id: pharmacyId || null,
},
});
} catch (e) {
console.log('Failed to unsubscribe from medicine notifications:', e);
}
}
export async function scheduleMedicineAvailabilityNotification(
medicineName: string,
pharmacyName: string
) {
await Notifications.scheduleNotificationAsync({
content: {
title: 'Medicamento disponible',
body: `${medicineName} está disponible en ${pharmacyName}`,
data: { type: 'medicine_availability' },
},
trigger: null,
});
}
export function addNotificationListener(
handler: (notification: Notifications.Notification) => void
): Notifications.EventSubscription {
return Notifications.addNotificationReceivedListener(handler);
}
export function addNotificationResponseListener(
handler: (response: Notifications.NotificationResponse) => void
): Notifications.EventSubscription {
return Notifications.addNotificationResponseReceivedListener(handler);
}