import React, { useEffect, useState, useContext, createContext } from 'react'; import * as Notifications from 'expo-notifications'; import { storage, StoreType } from 'src/storage'; import { AppState, AppStateStatus, Linking, Platform } from 'react-native'; import { CommonActions, useNavigation } from '@react-navigation/native'; import { NAVIGATION_PAGES } from 'src/types'; import { usePostSetSettingsMutation } from '@api/notifications'; import { useMessagesStore } from 'src/stores/unreadMessagesStore'; import { useFriendsNotificationsStore } from 'src/stores/friendsNotificationsStore'; import { handleNotificationData, registerBackgroundNotificationTask, unregisterBackgroundNotificationTask } from 'src/utils/pushNotificationTask'; const PushNotificationContext = createContext<{ isSubscribed: boolean; toggleSubscription: () => Promise; unsubscribeFromNotifications: () => Promise; }>({ isSubscribed: false, toggleSubscription: async () => {}, unsubscribeFromNotifications: async () => {} }); export const usePushNotification = () => useContext(PushNotificationContext); export const PushNotificationProvider = ({ children }: { children: React.ReactNode }) => { const token = storage.get('token', StoreType.STRING) as string; const [isSubscribed, setIsSubscribed] = useState( (storage.get('subscribed', StoreType.BOOLEAN) as boolean) ?? false ); const { mutateAsync: setNotificationsSettings } = usePostSetSettingsMutation(); const navigation = useNavigation(); const updateNotificationStatus = useFriendsNotificationsStore( (state) => state.updateNotificationStatus ); const updateUnreadMessagesCount = useMessagesStore((state) => state.updateUnreadMessagesCount); const [appState, setAppState] = useState(AppState.currentState); const lastNotificationResponse = Notifications.useLastNotificationResponse(); useEffect(() => { const handleAppStateChange = (nextAppState: AppStateStatus) => { if (appState.match(/inactive|background/) && nextAppState === 'active' && isSubscribed) { Notifications.getBadgeCountAsync().then((badgeCount) => { if (badgeCount > 0) { Notifications.setBadgeCountAsync(-1); } }); Notifications.getPresentedNotificationsAsync().then((notifications) => { if (Platform.OS === 'ios' && notifications.length > 0) { notifications.forEach((notification) => { let groupToken; let messageId; let fromUser; const parsedData = JSON.parse( (notification.request.trigger as Notifications.PushNotificationTrigger)?.payload ?.params as string ) ?? {}; groupToken = parsedData?.group_token; messageId = (notification.request.trigger as Notifications.PushNotificationTrigger) ?.payload?.message_id as number; fromUser = parsedData?.id; handleNotificationData(groupToken, messageId, fromUser); }); } }); updateNotificationStatus(); updateUnreadMessagesCount(); } setAppState(nextAppState); }; const subscription = AppState.addEventListener('change', handleAppStateChange); return () => { subscription.remove(); }; }, [appState]); useEffect(() => { if (lastNotificationResponse && Platform.OS === 'android') { const data = lastNotificationResponse.notification.request.content.data; if (data?.screen && data?.parentScreen) { if (data?.params) { const parsedParams = JSON.parse(data.params) ?? {}; navigation.dispatch( CommonActions.reset({ index: 1, routes: [ { name: 'DrawerApp', state: { routes: [ { name: data.parentScreen, state: { routes: [ { name: NAVIGATION_PAGES.MAP_TAB }, { name: data.screen, params: parsedParams } ] } } ] } } ] }) ); } else { navigation.dispatch( CommonActions.reset({ index: 1, routes: [ { name: 'DrawerApp', state: { routes: [ { name: data.parentScreen, state: { routes: [ { name: NAVIGATION_PAGES.MAP_TAB }, { name: data.screen } ] } } ] } } ] }) ); } } if (data?.url) { Linking.openURL(data.url); } } }, [lastNotificationResponse]); const checkNotificationPermissions = async () => { const { status } = await Notifications.getPermissionsAsync(); return status; }; useEffect(() => { const getPermissionsStatus = async () => { const status = await checkNotificationPermissions(); if (status !== 'granted' && isSubscribed) { await unsubscribeFromNotifications(); return; } }; if (isSubscribed) { getPermissionsStatus(); Notifications.setNotificationHandler({ handleNotification: async () => ({ shouldShowAlert: true, shouldPlaySound: false, shouldSetBadge: false }) }); registerBackgroundNotificationTask(); const notificationListener = Notifications.addNotificationReceivedListener((notification) => { updateNotificationStatus(); updateUnreadMessagesCount(); let groupToken; let messageId; let fromUser; if (Platform.OS === 'ios') { const parsedData = JSON.parse( (notification.request.trigger as Notifications.PushNotificationTrigger)?.payload ?.params as string ) ?? {}; groupToken = parsedData?.group_token; messageId = (notification.request.trigger as Notifications.PushNotificationTrigger) ?.payload?.message_id as number; fromUser = parsedData?.id; handleNotificationData(groupToken, messageId, fromUser); } }); const responseListener = Notifications.addNotificationResponseReceivedListener((response) => { let screenName; let url; let parentScreen; let params; if (Platform.OS === 'ios') { parentScreen = ( response.notification.request.trigger as Notifications.PushNotificationTrigger )?.payload?.parentScreen; screenName = ( response.notification.request.trigger as Notifications.PushNotificationTrigger )?.payload?.screen; params = (response.notification.request.trigger as Notifications.PushNotificationTrigger) ?.payload?.params; url = (response.notification.request.trigger as Notifications.PushNotificationTrigger) ?.payload?.url; } if (screenName && parentScreen) { if (params) { const parsedParams = JSON.parse(params as string) ?? {}; navigation.dispatch( CommonActions.reset({ index: 1, routes: [ { name: 'DrawerApp', state: { routes: [ { name: parentScreen as string, state: { routes: [ { name: NAVIGATION_PAGES.MAP_TAB }, { name: screenName as string, params: parsedParams } ] } } ] } } ] }) ); } else { navigation.dispatch( CommonActions.reset({ index: 1, routes: [ { name: 'DrawerApp', state: { routes: [ { name: parentScreen as string, state: { routes: [ { name: NAVIGATION_PAGES.MAP_TAB }, { name: screenName as string } ] } } ] } } ] }) ); } } if (url) { Linking.openURL(url as string); } }); Notifications.getBadgeCountAsync().then((badgeCount) => { if (badgeCount > 0) { Notifications.setBadgeCountAsync(-1); } }); return () => { notificationListener.remove(); responseListener.remove(); }; } else { unregisterBackgroundNotificationTask(); } }, [isSubscribed]); const subscribeToNotifications = async () => { storage.set('subscribed', true); setIsSubscribed(true); }; const unsubscribeFromNotifications = async () => { const settings = { [Platform.OS === 'ios' ? 'app-ios' : 'app-android']: 0 }; await setNotificationsSettings({ token, settings: JSON.stringify(settings) }); storage.remove('deviceToken'); storage.set('subscribed', false); setIsSubscribed(false); unregisterBackgroundNotificationTask(); }; const toggleSubscription = async () => { if (isSubscribed) { await unsubscribeFromNotifications(); } else { await subscribeToNotifications(); } }; return ( {children} ); };