diff --git a/packages/jsActions/mobile-resources-native/CHANGELOG.md b/packages/jsActions/mobile-resources-native/CHANGELOG.md index d8b191bd8..fdf6be1f7 100644 --- a/packages/jsActions/mobile-resources-native/CHANGELOG.md +++ b/packages/jsActions/mobile-resources-native/CHANGELOG.md @@ -6,7 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] +### Changed + +- We have migrated from `react-native-push-notification` to `@notifee/react-native` for better new architecture compatibility and enhanced push notification features. + ## [10.1.8] Native Mobile Resources - 2025-11-7 + ## [] Notifications ### BREAKING @@ -14,19 +19,21 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Updated the react-native-firebase to v20.1.0. Ensure that the Native Template and Native Builder are updated accordingly. ## [4.2.0] Notifications + ## BREAKING - Updated the react-native-firebase to v20.1.0. Ensure that the Native Template and Native Builder are updated accordingly. ## [10.1.6] Native Mobile Resources - 2025-10-17 - ## [2.0.1] Gallery + ### Fixed -- We've fixed an issue where the Gallery widget would not properly load more items when scrolling down quickly. +- We've fixed an issue where the Gallery widget would not properly load more items when scrolling down quickly. ## [6.1.2] VideoPlayer + - We have updated `react-native-video` version to 6.10.2. - We have fixed an issue where videos were muted in iOS silent mode. The video player now plays sound even when the device is set to silent. diff --git a/packages/jsActions/mobile-resources-native/package.json b/packages/jsActions/mobile-resources-native/package.json index 78116f923..795ae6312 100644 --- a/packages/jsActions/mobile-resources-native/package.json +++ b/packages/jsActions/mobile-resources-native/package.json @@ -26,8 +26,8 @@ "release:marketplace": "node ../../../scripts/release/marketplaceRelease.js" }, "dependencies": { + "@notifee/react-native": "9.1.8", "@react-native-camera-roll/camera-roll": "7.4.0", - "@react-native-community/push-notification-ios": "1.10.1", "@react-native-firebase/messaging": "20.1.0", "@swan-io/react-native-browser": "^0.4.1", "fbjs": "3.0.4", @@ -38,7 +38,6 @@ "react-native-image-picker": "7.2.3", "react-native-localize": "3.2.1", "react-native-permissions": "4.1.5", - "react-native-push-notification": "8.1.1", "react-native-schedule-exact-alarm-permission": "^0.1.3", "react-native-sound": "0.11.0", "react-native-touch-id": "4.4.1", @@ -54,4 +53,4 @@ "rimraf": "^4.4.1", "rollup": "^2.79.2" } -} \ No newline at end of file +} diff --git a/packages/jsActions/mobile-resources-native/src/notifications/CancelAllScheduledNotifications.ts b/packages/jsActions/mobile-resources-native/src/notifications/CancelAllScheduledNotifications.ts index f47fde625..e3e547625 100644 --- a/packages/jsActions/mobile-resources-native/src/notifications/CancelAllScheduledNotifications.ts +++ b/packages/jsActions/mobile-resources-native/src/notifications/CancelAllScheduledNotifications.ts @@ -5,8 +5,8 @@ // - the code between BEGIN USER CODE and END USER CODE // - the code between BEGIN EXTRA CODE and END EXTRA CODE // Other code you write will be lost the next time you deploy the project. -import { NativeModules, Platform } from "react-native"; -import PushNotification from "react-native-push-notification"; +import { NativeModules } from "react-native"; +import notifee from "@notifee/react-native"; // BEGIN EXTRA CODE // END EXTRA CODE @@ -17,13 +17,12 @@ import PushNotification from "react-native-push-notification"; */ export async function CancelAllScheduledNotifications(): Promise { // BEGIN USER CODE - // Documentation https://github.com/zo0r/react-native-push-notification - const isIOS = Platform.OS === "ios"; - if (NativeModules && isIOS && !NativeModules.RNCPushNotificationIOS) { - return Promise.reject(new Error("Notifications module is not available in your app")); + // Documentation https://github.com/invertase/notifee + if (NativeModules && !NativeModules.NotifeeApiModule) { + return Promise.reject(new Error("Notifee native module is not available in your app")); } - PushNotification.cancelAllLocalNotifications(); + notifee.cancelAllNotifications(); return Promise.resolve(); // END USER CODE diff --git a/packages/jsActions/mobile-resources-native/src/notifications/CancelScheduledNotification.ts b/packages/jsActions/mobile-resources-native/src/notifications/CancelScheduledNotification.ts index 20b1ab7b8..d840fc9cd 100644 --- a/packages/jsActions/mobile-resources-native/src/notifications/CancelScheduledNotification.ts +++ b/packages/jsActions/mobile-resources-native/src/notifications/CancelScheduledNotification.ts @@ -5,8 +5,8 @@ // - the code between BEGIN USER CODE and END USER CODE // - the code between BEGIN EXTRA CODE and END EXTRA CODE // Other code you write will be lost the next time you deploy the project. -import { NativeModules, Platform } from "react-native"; -import PushNotification from "react-native-push-notification"; +import { NativeModules } from "react-native"; +import notifee from "@notifee/react-native"; // BEGIN EXTRA CODE // END EXTRA CODE @@ -17,17 +17,16 @@ import PushNotification from "react-native-push-notification"; */ export async function CancelScheduledNotification(notificationId?: string): Promise { // BEGIN USER CODE - // Documentation https://github.com/zo0r/react-native-push-notification - const isIOS = Platform.OS === "ios"; - if (NativeModules && isIOS && !NativeModules.RNCPushNotificationIOS) { - return Promise.reject(new Error("Notifications module is not available in your app")); + // Documentation Documentation https://github.com/invertase/notifee + if (NativeModules && !NativeModules.NotifeeApiModule) { + return Promise.reject(new Error("Notifee native module is not available in your app")); } if (!notificationId) { return Promise.reject(new Error("Input parameter 'Notification id' is required")); } - PushNotification.cancelLocalNotification(notificationId); + notifee.cancelNotification(notificationId); return Promise.resolve(); // END USER CODE diff --git a/packages/jsActions/mobile-resources-native/src/notifications/ClearAllDeliveredNotifications.ts b/packages/jsActions/mobile-resources-native/src/notifications/ClearAllDeliveredNotifications.ts index ba197fdfc..cd0debaed 100644 --- a/packages/jsActions/mobile-resources-native/src/notifications/ClearAllDeliveredNotifications.ts +++ b/packages/jsActions/mobile-resources-native/src/notifications/ClearAllDeliveredNotifications.ts @@ -5,8 +5,8 @@ // - the code between BEGIN USER CODE and END USER CODE // - the code between BEGIN EXTRA CODE and END EXTRA CODE // Other code you write will be lost the next time you deploy the project. -import { NativeModules, Platform } from "react-native"; -import PushNotification from "react-native-push-notification"; +import { NativeModules } from "react-native"; +import notifee from "@notifee/react-native"; // BEGIN EXTRA CODE // END EXTRA CODE @@ -17,13 +17,12 @@ import PushNotification from "react-native-push-notification"; */ export async function ClearAllDeliveredNotifications(): Promise { // BEGIN USER CODE - // Documentation https://github.com/zo0r/react-native-push-notification - const isIOS = Platform.OS === "ios"; - if (NativeModules && isIOS && !NativeModules.RNCPushNotificationIOS) { - return Promise.reject(new Error("Notifications module is not available in your app")); + // Documentation Documentation https://github.com/invertase/notifee + if (NativeModules && !NativeModules.NotifeeApiModule) { + return Promise.reject(new Error("Notifee native module is not available in your app")); } - PushNotification.removeAllDeliveredNotifications(); + notifee.cancelAllNotifications(); return Promise.resolve(); // END USER CODE diff --git a/packages/jsActions/mobile-resources-native/src/notifications/DisplayNotification.ts b/packages/jsActions/mobile-resources-native/src/notifications/DisplayNotification.ts index de1077f8e..bbfff2e5f 100644 --- a/packages/jsActions/mobile-resources-native/src/notifications/DisplayNotification.ts +++ b/packages/jsActions/mobile-resources-native/src/notifications/DisplayNotification.ts @@ -6,7 +6,7 @@ // - the code between BEGIN EXTRA CODE and END EXTRA CODE // Other code you write will be lost the next time you deploy the project. import { NativeModules, Platform } from "react-native"; -import PushNotification, { PushNotificationObject } from "react-native-push-notification"; +import notifee, { AndroidChannel, AndroidImportance, Notification } from "@notifee/react-native"; // BEGIN EXTRA CODE // END EXTRA CODE @@ -32,59 +32,54 @@ export async function DisplayNotification( actionGuid?: string ): Promise { // BEGIN USER CODE - // Documentation https://github.com/zo0r/react-native-push-notification - - const isIOS = Platform.OS === "ios"; - if (NativeModules && isIOS && !NativeModules.RNCPushNotificationIOS) { - return Promise.reject(new Error("Notifications module is not available in your app")); + if (!body) { + throw new Error("Input parameter 'Body' is required"); } - if (!body) { - return Promise.reject(new Error("Input parameter 'Body' is required")); + // Documentation Documentation https://github.com/invertase/notifee + if (NativeModules && !NativeModules.NotifeeApiModule) { + return Promise.reject(new Error("Notifee native module is not available in your app")); } - const notification = { message: body } as PushNotificationObject; + const channelId = playSound ? "mendix-local-notifications-withsound" : "mendix-local-notifications"; + await createNotificationChannelIfNeeded(channelId); - if (!isIOS) { - const channelId = "mendix-local-notifications"; - const channelExists = await new Promise(resolve => - PushNotification.channelExists(channelId, (exists: boolean) => resolve(exists)) - ); - if (!channelExists) { - const channel = await new Promise(resolve => - PushNotification.createChannel( - { - channelId, - channelName: "Local notifications" - }, - created => resolve(created) - ) - ); - if (!channel) { - return Promise.reject(new Error("Could not create the local notifications channel")); - } - } - notification.channelId = channelId; - } + const notification: Notification = { + title: title || undefined, + body, + android: { channelId, sound: "default" }, + ios: playSound ? { sound: "default" } : {} + }; - if (title) { - notification.title = title; + if (subtitle && Platform.OS === "ios") { + notification.subtitle = subtitle; } - if (subtitle && !isIOS) { - notification.subText = subtitle; + if (actionName || actionGuid) { + notification.data = { + actionName: actionName ?? "", + guid: actionGuid ?? "" + }; } - notification.playSound = !!playSound; + await notifee.displayNotification(notification); - if (actionName || actionGuid) { - notification.userInfo = { - actionName, - guid: actionGuid + async function createNotificationChannelIfNeeded(channelId: string): Promise { + if (Platform.OS === "ios") { + return; + } + const existingChannel = await notifee.getChannel(channelId); + const channel: AndroidChannel = { + id: channelId, + name: "Local Notifications", + importance: AndroidImportance.HIGH, + ...(playSound ? { sound: "default" } : {}) }; + if (existingChannel === null) { + await notifee.createChannel(channel); + } } - PushNotification.localNotification(notification); return Promise.resolve(); // END USER CODE } diff --git a/packages/jsActions/mobile-resources-native/src/notifications/ScheduleNotification.ts b/packages/jsActions/mobile-resources-native/src/notifications/ScheduleNotification.ts index bf7e445ba..3d8e89e53 100644 --- a/packages/jsActions/mobile-resources-native/src/notifications/ScheduleNotification.ts +++ b/packages/jsActions/mobile-resources-native/src/notifications/ScheduleNotification.ts @@ -6,7 +6,13 @@ // - the code between BEGIN EXTRA CODE and END EXTRA CODE // Other code you write will be lost the next time you deploy the project. import { NativeModules, Platform } from "react-native"; -import PushNotification, { PushNotificationScheduleObject } from "react-native-push-notification"; +import notifee, { + AndroidChannel, + AndroidImportance, + Notification, + TimestampTrigger, + TriggerType +} from "@notifee/react-native"; // BEGIN EXTRA CODE // END EXTRA CODE @@ -36,68 +42,64 @@ export async function ScheduleNotification( actionGuid?: string ): Promise { // BEGIN USER CODE - // Documentation https://github.com/zo0r/react-native-push-notification - - const isIOS = Platform.OS === "ios"; - if (NativeModules && isIOS && !NativeModules.RNCPushNotificationIOS) { - return Promise.reject(new Error("Notifications module is not available in your app")); - } - if (!body) { - return Promise.reject(new Error("Input parameter 'Body' is required")); + throw new Error("Input parameter 'Body' is required"); } - const notification = { message: body } as PushNotificationScheduleObject; - const notificationIdNumber = Number(notificationId); - - if (!isIOS) { - const channelId = "mendix-local-notifications"; - const channelExists = await new Promise(resolve => - PushNotification.channelExists(channelId, (exists: boolean) => resolve(exists)) - ); - if (!channelExists) { - const channel = await new Promise(resolve => - PushNotification.createChannel( - { - channelId, - channelName: "Local notifications" - }, - created => resolve(created) - ) - ); - if (!channel) { - return Promise.reject(new Error("Could not create the local notifications channel")); - } - } - notification.channelId = channelId; + if (!date) { + throw new Error("Input parameter 'Date' is required"); } - if (notificationIdNumber) { - notification.id = notificationIdNumber; + // Documentation Documentation https://github.com/invertase/notifee + if (NativeModules && !NativeModules.NotifeeApiModule) { + return Promise.reject(new Error("Notifee native module is not available in your app")); } - if (title) { - notification.title = title; - } + const channelId = playSound ? "mendix-local-notifications-withsound" : "mendix-local-notifications"; + await createNotificationChannelIfNeeded(channelId); - if (subtitle && !isIOS) { - notification.subText = subtitle; - } + const notification: Notification = { + id: notificationId, + title: title || undefined, + body, + android: { channelId, sound: "default" }, + ios: playSound ? { sound: "default" } : {} + }; - notification.playSound = !!playSound; + if (subtitle && Platform.OS === "ios") { + notification.subtitle = subtitle; + } if (actionName || actionGuid) { - notification.userInfo = { - actionName, - guid: actionGuid + notification.data = { + actionName: actionName ?? "", + guid: actionGuid ?? "" }; } - if (date && date.getTime()) { - notification.date = date; + const trigger: TimestampTrigger = { + type: TriggerType.TIMESTAMP, + timestamp: date.getTime() + }; + + await notifee.createTriggerNotification(notification, trigger); + + async function createNotificationChannelIfNeeded(channelId: string): Promise { + if (Platform.OS === "ios") { + return; + } + const existingChannel = await notifee.getChannel(channelId); + const channel: AndroidChannel = { + id: channelId, + name: "Local Notifications", + importance: AndroidImportance.HIGH, + ...(playSound ? { sound: "default" } : {}) + }; + if (existingChannel === null) { + await notifee.createChannel(channel); + } } - PushNotification.localNotificationSchedule(notification); return Promise.resolve(); // END USER CODE } diff --git a/packages/jsActions/mobile-resources-native/src/notifications/SetBadgeNumber.ts b/packages/jsActions/mobile-resources-native/src/notifications/SetBadgeNumber.ts index 30752a575..e5e80a248 100644 --- a/packages/jsActions/mobile-resources-native/src/notifications/SetBadgeNumber.ts +++ b/packages/jsActions/mobile-resources-native/src/notifications/SetBadgeNumber.ts @@ -6,8 +6,8 @@ // - the code between BEGIN EXTRA CODE and END EXTRA CODE // Other code you write will be lost the next time you deploy the project. import { Big } from "big.js"; -import { NativeModules, Platform } from "react-native"; -import PushNotification from "react-native-push-notification"; +import { NativeModules } from "react-native"; +import notifee from "@notifee/react-native"; // BEGIN EXTRA CODE // END EXTRA CODE @@ -18,11 +18,10 @@ import PushNotification from "react-native-push-notification"; */ export async function SetBadgeNumber(badgeNumber?: Big): Promise { // BEGIN USER CODE - // Documentation https://github.com/zo0r/react-native-push-notification + // Documentation Documentation https://github.com/invertase/notifee - const isIOS = Platform.OS === "ios"; - if (NativeModules && isIOS && !NativeModules.RNCPushNotificationIOS) { - return Promise.reject(new Error("Notifications module is not available in your app")); + if (NativeModules && !NativeModules.NotifeeApiModule) { + return Promise.reject(new Error("Notifee native module is not available in your app")); } if (!badgeNumber) { @@ -33,7 +32,7 @@ export async function SetBadgeNumber(badgeNumber?: Big): Promise { return Promise.reject(new Error("Input parameter 'Badge number' should be zero or greater")); } - return PushNotification.setApplicationIconBadgeNumber(Number(badgeNumber)); + return notifee.setBadgeCount(Number(badgeNumber)); // END USER CODE } diff --git a/packages/jsActions/nanoflow-actions-native/package.json b/packages/jsActions/nanoflow-actions-native/package.json index 090b1d5fc..66e6603fa 100644 --- a/packages/jsActions/nanoflow-actions-native/package.json +++ b/packages/jsActions/nanoflow-actions-native/package.json @@ -36,4 +36,4 @@ "mendix": "~10.0.9976", "rollup": "^2.79.2" } -} \ No newline at end of file +} diff --git a/packages/pluggableWidgets/notifications-native/CHANGELOG.md b/packages/pluggableWidgets/notifications-native/CHANGELOG.md index 0746b7ee0..ebebdda4d 100644 --- a/packages/pluggableWidgets/notifications-native/CHANGELOG.md +++ b/packages/pluggableWidgets/notifications-native/CHANGELOG.md @@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] +## [4.3.0] - 2025-11-21 + +## BREAKING + +- Migrated from react-native-push-notification to notifee. + ## [4.2.0] - 2025-11-7 ## BREAKING diff --git a/packages/pluggableWidgets/notifications-native/package.json b/packages/pluggableWidgets/notifications-native/package.json index db3c0202e..76e50683b 100644 --- a/packages/pluggableWidgets/notifications-native/package.json +++ b/packages/pluggableWidgets/notifications-native/package.json @@ -1,7 +1,7 @@ { "name": "notifications-native", "widgetName": "Notifications", - "version": "4.2.0", + "version": "4.3.0", "license": "Apache-2.0", "repository": { "type": "git", @@ -21,7 +21,7 @@ "@mendix/piw-utils-internal": "*", "@react-native-firebase/app": "20.1.0", "@react-native-firebase/messaging": "20.1.0", - "react-native-push-notification": "8.1.1" + "@notifee/react-native": "9.1.8" }, "devDependencies": { "@mendix/pluggable-widgets-tools": "~10.0.1", diff --git a/packages/pluggableWidgets/notifications-native/src/Notifications.tsx b/packages/pluggableWidgets/notifications-native/src/Notifications.tsx index 9518cfdf9..1d71686f0 100644 --- a/packages/pluggableWidgets/notifications-native/src/Notifications.tsx +++ b/packages/pluggableWidgets/notifications-native/src/Notifications.tsx @@ -1,19 +1,14 @@ import messaging, { FirebaseMessagingTypes } from "@react-native-firebase/messaging"; -import PushNotification, { ReceivedNotification } from "react-native-push-notification"; import { executeAction } from "@mendix/piw-utils-internal"; import { useCallback, useEffect, useRef, useState } from "react"; import { Platform } from "react-native"; import { ActionValue, ValueStatus, Option } from "mendix"; import "@react-native-firebase/app"; +import notifee, { EventType } from "@notifee/react-native"; import { ActionsType, NotificationsProps } from "../typings/NotificationsProps"; // re-declare the library's type because: 1) it doesn't match library version 2) the definition file exports two symbols with same name. -interface IPushNotification extends ReceivedNotification { - title: string; - message: string; -} - interface ActionData { actionName?: string; guid?: string; @@ -110,22 +105,27 @@ export function Notifications(props: NotificationsProps): null { // wait for all used DynamicValues are available before configuring, else handleNotification is invoked while // properties in scope are loading. if (loadNotifications) { - PushNotification.configure({ - // called when user taps local notification - onNotification(notification: IPushNotification) { - const messageId = notification.data[Platform.OS === "ios" ? "gcm.message_id" : "google.message_id"]; + // Handle notifications when the app is in the foreground + notifee.onForegroundEvent(({ type, detail }) => { + if (type === EventType.PRESS) { + const notification = detail.notification; + if (notification === undefined || notification.data === undefined) { + console.log("notificaiton is not exist"); + return; + } + const messageId = + notification.data?.[Platform.OS === "ios" ? "gcm.message_id" : "google.message_id"]; handleNotification( { data: notification.data, title: notification.title, - body: notification.message, - subTitle: notification.subText + body: notification.body, + subTitle: notification.subtitle }, action => action.onOpen, - messageId + messageId as any ); - }, - requestPermissions: Platform.OS === "ios" + } }); } }, [loadNotifications, handleNotification]); diff --git a/packages/pluggableWidgets/notifications-native/src/package.xml b/packages/pluggableWidgets/notifications-native/src/package.xml index d21473de1..4f1d98410 100644 --- a/packages/pluggableWidgets/notifications-native/src/package.xml +++ b/packages/pluggableWidgets/notifications-native/src/package.xml @@ -1,6 +1,6 @@ - + diff --git a/yarn.lock b/yarn.lock index ec1efb26c..a145dfc1b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2519,6 +2519,15 @@ __metadata: languageName: node linkType: hard +"@notifee/react-native@npm:9.1.8": + version: 9.1.8 + resolution: "@notifee/react-native@npm:9.1.8" + peerDependencies: + react-native: "*" + checksum: 10/7c5237fba99906d8da02146afbfe8ff6de9f4047eecd806dea889bfd76b1cfe7ad3c181881bf2729aa348b83e035f4d02cfc3ac8eb8c4ba3a4415b445b439c38 + languageName: node + linkType: hard + "@npmcli/agent@npm:^3.0.0": version: 3.0.0 resolution: "@npmcli/agent@npm:3.0.0" @@ -2918,18 +2927,6 @@ __metadata: languageName: node linkType: hard -"@react-native-community/push-notification-ios@npm:1.10.1": - version: 1.10.1 - resolution: "@react-native-community/push-notification-ios@npm:1.10.1" - dependencies: - invariant: "npm:^2.2.4" - peerDependencies: - react: ">=16.6.3" - react-native: ">=0.58.4" - checksum: 10/5b02066ebad4200957bbb60634d42f4a2ea647d9d84d69adcf6fa71acf72063110d2f7b7b1e45d4153d94a3592806f5de9626fb33897707197c03a14ea299a55 - languageName: node - linkType: hard - "@react-native-firebase/app@npm:20.1.0": version: 20.1.0 resolution: "@react-native-firebase/app@npm:20.1.0" @@ -12035,8 +12032,8 @@ __metadata: resolution: "mobile-resources-native@workspace:packages/jsActions/mobile-resources-native" dependencies: "@mendix/pluggable-widgets-tools": "npm:^10.0.1" + "@notifee/react-native": "npm:9.1.8" "@react-native-camera-roll/camera-roll": "npm:7.4.0" - "@react-native-community/push-notification-ios": "npm:1.10.1" "@react-native-firebase/messaging": "npm:20.1.0" "@swan-io/react-native-browser": "npm:^0.4.1" "@types/querystringify": "npm:^2.0.0" @@ -12052,7 +12049,6 @@ __metadata: react-native-image-picker: "npm:7.2.3" react-native-localize: "npm:3.2.1" react-native-permissions: "npm:4.1.5" - react-native-push-notification: "npm:8.1.1" react-native-schedule-exact-alarm-permission: "npm:^0.1.3" react-native-sound: "npm:0.11.0" react-native-touch-id: "npm:4.4.1"