The push notification does not link to the screen

When I launch the application via Expo Go and test the notification, the target screen opens after clicking on it. However, when I build the APK and run it on a physical device, nothing happens when I click on the received notification.

When I check the log through Android Studio / DEBUG, I get the following:

2024-08-08 23:44:01.315 24487-24506 EGL_emulation           my.app.package.name  D  app_time_stats: avg=33.51ms min=12.44ms max=72.02ms count=27
2024-08-08 23:44:01.836 24487-24491 my.app.package.name     I  NativeAlloc concurrent mark compact GC freed 7901KB AllocSpace bytes, 30(936KB) LOS objects, 49% free, 11MB/23MB, paused 10.083ms,2.385ms total 299.526ms
2024-08-08 23:44:03.446 24487-24506 EGL_emulation           my.app.package.name  D  app_time_stats: avg=97.43ms min=3.60ms max=1518.18ms count=21
2024-08-08 23:44:04.481 24487-24506 EGL_emulation           my.app.package.name  D  app_time_stats: avg=15.67ms min=2.44ms max=38.54ms count=45
2024-08-08 23:44:05.517 24487-24506 EGL_emulation           my.app.package.name  D  app_time_stats: avg=53.96ms min=4.06ms max=572.25ms count=18
2024-08-08 23:44:05.578 24487-24649 notifications           my.app.package.name  E  Couldn't get channel for the notifications - trigger is 'null'. Fallback to 'expo_notifications_fallback_notification_channel' channel
2024-08-08 23:44:06.933 24487-24506 EGL_emulation           my.app.package.name  D  app_time_stats: avg=109.35ms min=2.31ms max=1171.37ms count=12
2024-08-08 23:44:08.011 24487-24506 EGL_emulation           my.app.package.name  D  app_time_stats: avg=31.38ms min=3.04ms max=228.85ms count=29
2024-08-08 23:44:09.094 24487-24487 VRI[MainActivity]       my.app.package.name  D  visibilityChanged oldVisibility=true newVisibility=false
2024-08-08 23:44:09.227 24487-24559 ReactNativeJNI          my.app.package.name  I  Memory warning (pressure level: TRIM_MEMORY_UI_HIDDEN) received by JS VM, ignoring because it's non-severe
2024-08-08 23:44:11.632 24487-24487 ReactNativeJS           my.app.package.name  D  [native] ExpoNotificationLifecycleListener contains an unmarshaled notification response. Skipping.
2024-08-08 23:44:11.650 24487-24559 ReactNativeJS           my.app.package.name  W  Event data is missing in the notification payload.
2024-08-08 23:44:11.650 24487-24559 ReactNativeJS           my.app.package.name  W  Event data is missing in the notification payload.
2024-08-08 23:44:13.298 24487-24506 EGL_emulation           my.app.package.name  D  app_time_stats: avg=71.31ms min=13.70ms max=216.31ms count=17
2024-08-08 23:44:16.131 24487-24506 EGL_emulation           my.app.package.name  D  app_time_stats: avg=89.93ms min=3.68ms max=2012.98ms count=30
2024-08-08 23:44:17.139 24487-24506 EGL_emulation           my.app.package.name  D  app_time_stats: avg=16.40ms min=3.34ms max=37.08ms count=45
2024-08-08 23:44:19.622 24487-24506 EGL_emulation           my.app.package.name  D  app_time_stats: avg=139.47ms min=3.89ms max=2030.02ms count=17
2024-08-08 23:44:20.639 24487-24506 EGL_emulation           my.app.package.name  D  app_time_stats: avg=26.14ms min=6.18ms max=69.69ms count=30

What I get when I do the same process in the Expo Go environment locally:

and requesting notification permissions...
 LOG  Resources loaded, navigation is ready.
 LOG  ExponentPushToken[anamAFPyKU4GsU7YpbF20f]
 LOG  Scheduling notifications for event: {"field1": "0.00", "field2": "2024-08-08", "field3": "XX.XXXXXX", "field4": "some_value", "field5": "XX.XXXXX", "field6": "another_value", "field7": "yet_another_value", "field8": "123123123", "field9": "20:00:00", "field10": "12:00:00", "field11": "2", "field12": "../public/template/images/placeholder/1.jpg"}
 LOG  Scheduling immediate notification: {"attachments": [{"url": "https://example.com/admin/public/template/images/placeholder/1.jpg"}], "body": "Notification message.", "data": {"event": {"field1": "0.00", "field2": "2024-08-08", "field3": "XX.XXXXX", "field4": "some_value", "field5": "XX.XXXXX", "field6": "another_value", "field7": "yet_another_value", "field8": "123123123", "field9": "20:00:00", "field10": "12:00:00", "active": undefined, "field11": "2", "id": "2", "image": "../public/template/images/placeholder/1", "imagePath": "../public/template/images/placeholder/1", "field12": XX.XXXXX, "field13": "some_value", "field14": XX.XXXXX, "field15": "another_value", "field16": "123123123", "field17": "0.00", "field18": "12:00:00 - 20:00:00"}}, "sound": "default", "title": "Notification title header"}       
 LOG  Notification response: {
  "notification": {
    "request": {
      "trigger": null,
      "content": {
        "title": "Header title",
        "badge": null,
        "autoDismiss": true,
        "data": {
          "event": {
            "field1": "XX.XXXXX",
            "field2": "2",
            "field3": "some_value",
            "field4": "0.00",
            "field5": "0.00",
            "field6": "123123123",
            "field7": "../public/template/images/placeholder/1.jpg",
            "field8": "2024-08-08",
            "field9": "another_value",
            "field10": "2024-08-08",
            "field11": "12:00:00 - 20:00:00",
            "field12": XX.XXXXX,
            "field13": "yet_another_value",
            "field14": "another_value",
            "id": "2",
            "field15": "20:00:00",
            "field16": "123123123",
            "field17": "some_value",
            "field18": "XX.XXXXX",
            "field19": "12:00:00",
            "field20": "../public/template/images/placeholder/1.jpg",
            "field21": XX.XXXXX,
            "field22": "another_value"
          }
        },
        "body": "Notification message",       
        "sound": "custom",
        "sticky": false,
        "subtitle": null
      },
      "identifier": "285406aa-4d79-4889-85c7-8f162eed835e"
    },
    "date": 1723155191850
  },
  "actionIdentifier": "expo.modules.notifications.actions.DEFAULT"
}
 LOG  Notification response: {
  "notification": {
    "request": {
      "trigger": null,
      "content": {
        "title": "example",
        "badge": null,
        "autoDismiss": true,
        "data": {
          "event": {
            "field1": "XX.XXXXX",
            "field2": "2",
            "field3": "some_value",
            "field4": "0.00",
            "field5": "0.00",
            "field6": "123123123",
            "field7": "../public/template/images/placeholder/1.jpg",
            "field8": "2024-08-08",
            "field9": "another_value",
            "field10": "2024-08-08",
            "field11": "12:01:00 - 20:00:00",
            "field12": XX.XXXXX,
            "field13": "yet_another_value",
            "field14": "another_value",
            "id": "2",
            "field15": "20:00:00",
            "field16": "123123123",
            "field17": "some_value",
            "field18": "XX.XXXXX",
            "field19": "12:01:00",
            "field20": "../public/template/images/placeholder/1.jpg",
            "field21": XX.XXXXX,
            "field22": "another_value"
          }
        },
        "body": "Notification message",       
        "sound": "custom",
        "sticky": false,
        "subtitle": null
      },
      "identifier": "285406aa-4d79-4889-85c7-8f162eed835e"
    },
    "date": 1723155191850
  },
  "actionIdentifier": "expo.modules.notifications.actions.DEFAULT"
}
 LOG  Notification response received: {"actionIdentifier": "expo.modules.notifications.actions.DEFAULT", "notification": {"date": 1723155191850, "request": {"content": [Object], "identifier": "285406aa-4d79-4889-85c7-8f162eed835e", "trigger": null}}}

And when I launch the application through Expo Go and click on the notification, it opens the screen I need, while nothing opens in production.

App.js:

import React, { useEffect, useState, useRef, useCallback } from 'react';
import * as SplashScreen from 'expo-splash-screen';
import * as Font from 'expo-font';
import Navigation from './Components/StackNavigation';
import * as Notifications from 'expo-notifications';
import { useNavigationContainerRef } from '@react-navigation/native';
import * as Linking from 'expo-linking';
import { Platform, Alert } from 'react-native';

if (process.env.NODE_ENV !== 'development') {
  console.log = () => {};
}

Notifications.setNotificationHandler({
  handleNotification: async () => ({
    shouldShowAlert: true,
    shouldPlaySound: true,
    shouldSetBadge: true,
  }),
});

SplashScreen.preventAutoHideAsync();

const registerForPushNotificationsAsync = async () => {
  let token;
  if (Platform.OS === 'android') {
    await Notifications.setNotificationChannelAsync('default', {
      name: 'default',
      importance: Notifications.AndroidImportance.MAX,
      vibrationPattern: [0, 250, 250, 250],
      lightColor: '#FF231F7C',
    });
  }

  const { status: existingStatus } = await Notifications.getPermissionsAsync();
  let finalStatus = existingStatus;
  if (existingStatus !== 'granted') {
    const { status } = await Notifications.requestPermissionsAsync();
    finalStatus = status;
  }
  if (finalStatus !== 'granted') {
    Alert.alert("Permissions required", "You need to enable notifications permissions.");
    return;
  }

  token = (await Notifications.getExpoPushTokenAsync()).data;
  console.log(token);

  return token;
};

export default function App() {
  const [fontsLoaded, setFontsLoaded] = useState(false);
  const navigationRef = useNavigationContainerRef();
  const isNavigationReady = useRef(false);
  const pendingNotificationResponse = useRef(null);

  const prefix = Linking.createURL('/');

  const handleNotificationResponse = useCallback((response) => {
    console.log('Notification response:', JSON.stringify(response, null, 2));
    const event = response.notification.request.content.data?.event;
    if (event) {
      if (isNavigationReady.current) {
        navigationRef.current?.navigate('Event Details', { event });
      } else {
        pendingNotificationResponse.current = response;
      }
    } else {
      console.warn('Event data is missing in the notification payload.');
    }
  }, []);

  useEffect(() => {
    registerForPushNotificationsAsync();

    const subscription = Notifications.addNotificationResponseReceivedListener(handleNotificationResponse);
    return () => subscription.remove();
  }, [handleNotificationResponse]);

  useEffect(() => {
    async function loadResourcesAndDataAsync() {
      try {
        console.log('Loading fonts and requesting notification permissions...');
        await Font.loadAsync({
          // Load fonts
        });

        const { status } = await Notifications.requestPermissionsAsync();
        if (status !== 'granted') {
          alert('Sorry, we need notification permissions to make this work!');
        }
      } catch (e) {
        console.warn(e);
      } finally {
        console.log('Resources loaded, navigation is ready.');
        setFontsLoaded(true);
        SplashScreen.hideAsync();
        isNavigationReady.current = true;

        if (pendingNotificationResponse.current) {
          handleNotificationResponse(pendingNotificationResponse.current);
          pendingNotificationResponse.current = null;
        }
      }
    }

    loadResourcesAndDataAsync();
  }, [handleNotificationResponse]);

  if (!fontsLoaded) {
    return null;
  }

  return (
    <Navigation
      ref={navigationRef}
      linking={{
        prefixes: [prefix],
        config: {
          screens: {
            EventDetails: 'Event Details',
          },
        },
      }}
    />
  );
}

A screen that contains the context of notifications, etc…

import * as Notifications from 'expo-notifications';

// Set up the notification handler
Notifications.setNotificationHandler({
  handleNotification: async () => ({
    shouldShowAlert: true,
    shouldPlaySound: true,
    shouldSetBadge: true,
  }),
});

// Function to request notification permissions
const requestNotificationPermissions = async () => {
  const { status } = await Notifications.requestPermissionsAsync();
  if (status !== 'granted') {
    Alert.alert("Permissions required", "You need to enable notifications permissions.");
  }
};

// Function to clean image URLs
const cleanUrl = (url) => {
  const urlParts = url.split('/public/template/images/').pop();
  return `${config.BASE_URL}project-login/public/template/images/${urlParts}`;
};

// Function to schedule notifications for events
const scheduleNotifications = async (events) => {
  const now = moment();

  for (const event of events) {
    const eventStartDate = moment(event.date).set({
      hour: parseInt(event.startTime.split(':')[0]),
      minute: parseInt(event.startTime.split(':')[1]),
    });

    const oneDayBeforeEvent = eventStartDate.clone().subtract(1, 'days');
    const oneHourBeforeEvent = eventStartDate.clone().subtract(1, 'hours');

    console.log('Scheduling notifications for event:', event);

    if (oneDayBeforeEvent.isAfter(now)) {
      const notificationContent24h = {
        title: event.name,
        body: `Reminder: "${event.name}" starts in 24 hours at ${moment(event.startTime, 'HH:mm:ss').format('HH:mm')}!`,
        data: { event },
        sound: 'default',
        attachments: [{
          url: cleanUrl(event.imagePath)
        }],
      };
      console.log('Notification content 24h:', notificationContent24h);
      await Notifications.scheduleNotificationAsync({
        content: notificationContent24h,
        trigger: {
          date: oneDayBeforeEvent.toDate(),
        },
      });
    }

    if (oneHourBeforeEvent.isAfter(now)) {
      const notificationContent1h = {
        title: event.name,
        body: `Reminder: "${event.name}" starts in 1 hour at ${moment(event.startTime, 'HH:mm:ss').format('HH:mm')}!`,
        data: { event },
        sound: 'default',
        attachments: [{
          url: cleanUrl(event.imagePath)
        }],
      };
      console.log('Notification content 1h:', notificationContent1h);
      await Notifications.scheduleNotificationAsync({
        content: notificationContent1h,
        trigger: {
          date: oneHourBeforeEvent.toDate(),
        },
      });
    }
  }
};

// Effect to handle incoming notification responses
useEffect(() => {
  const subscription = Notifications.addNotificationResponseReceivedListener(response => {
    console.log('Notification response received:', response);
    if (response.notification.request.content.data && response.notification.request.content.data.event) {
      const { event } = response.notification.request.content.data;
      navigation.navigate('Event Details', { event });
    } else {
      console.warn('Event data is missing in the notification payload.');
    }
  });

  return () => subscription.remove();
}, [navigation]);

// Function to trigger an immediate notification
const handleEventNotification = async () => {
  if (savedEvents.length > 0) {
    const event = savedEvents[Math.floor(Math.random() * savedEvents.length)];
    const eventDate = moment(event.date).format('DD. MMMM YYYY');
    const eventTime = moment(event.startTime, 'HH:mm:ss').format('HH:mm');

    const notificationContent = {
      title: "Event Reminder",
      body: `Reminder: "${event.name}" starts at ${eventTime} on ${eventDate}.`,
      data: { 
        event: {
          ...event,
          id: event.event_id,
          name: event.name,
          description: event.description,
          date: event.date,
          active: event.status,
          time: `${event.startTime} - ${event.endTime}`,
          location: event.location,
          latitude: parseFloat(event.latitude), 
          longitude: parseFloat(event.longitude),  
          phone: event.phone,
          image: event.imagePath,
          price: event.ticketPrice
        }
      },
      sound: 'default',
      attachments: [{
        url: cleanUrl(`${config.BASE_URL}project-login/public/template/images/events/${event.imagePath}`)
      }],
    };
    console.log('Scheduling immediate notification:', notificationContent);
    await Notifications.scheduleNotificationAsync({
      content: notificationContent,
      trigger: null,
    });
  } else {
    Alert.alert("No events", "You have no saved events.");
  }
};

// Function to trigger a notification one hour before the event
const handleEventNotificationOneHour = async () => {
  if (savedEvents.length > 0) {
    const event = savedEvents[Math.floor(Math.random() * savedEvents.length)];
    const eventDate = moment(event.date).format('DD. MMMM YYYY');
    const eventTime = moment(event.startTime, 'HH:mm:ss').format('HH:mm');

    const notificationContent = {
      title: "Event Reminder",
      body: `Reminder: "${event.name}" starts in 1 hour at ${eventTime} on ${eventDate}.`,
      data: { 
        event: {
          ...event,
          id: event.event_id,
          name: event.name,
          description: event.description,
          date: event.date,
          active: event.status,
          time: `${event.startTime} - ${event.endTime}`,
          location: event.location,
          latitude: parseFloat(event.latitude),
          longitude: parseFloat(event.longitude),
          phone: event.phone,
          image: event.imagePath,
          price: event.ticketPrice
        }
      },
      sound: 'default',
      attachments: [{
        url: cleanUrl(`${config.BASE_URL}project-login/public/template/images/events/${event.imagePath}`)
      }],
    };
    console.log('Scheduling immediate notification (one hour before):', notificationContent);
    await Notifications.scheduleNotificationAsync({
      content: notificationContent,
      trigger: null,
    });
  } else {
    Alert.alert("No events", "You have no saved events.");
  }
};

New contributor

John is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật