diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index 05d1fccb..9095189a 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -1,4 +1,4 @@ -# React Native v5.0.0-beta-01 Migration Guide +# React Native v5.0.0-beta-02 Migration Guide # Intro @@ -35,14 +35,15 @@ The React Native SDK accesses the OneSignal native iOS and Android SDKs. For thi The OneSignal SDK has been updated to be more modular in nature. The SDK has been split into namespaces, and functionality previously in the static `OneSignal` class has been moved to the appropriate namespace. The namespaces and how to access them in code are as follows: -| **Namespace** | **Access Pattern** | -| ------------- | ----------------------------- | -| Debug | `OneSignal.Debug` | -| InAppMessages | `OneSignal.InAppMessages` | -| Location | `OneSignal.Location` | -| Notifications | `OneSignal.Notifications` | -| Session | `OneSignal.Session` | -| User | `OneSignal.User` | +| **Namespace** | **Access Pattern** | +| ------------- | -------------------------- | +| Debug | `OneSignal.Debug` | +| InAppMessages | `OneSignal.InAppMessages` | +| LiveActivities | `OneSignal.LiveActivities` | +| Location | `OneSignal.Location` | +| Notifications | `OneSignal.Notifications` | +| Session | `OneSignal.Session` | +| User | `OneSignal.User` | ## Initialization @@ -60,7 +61,7 @@ Replace the following: To the match the new initialization: ```typescript - OneSignal.init("YOUR_ONESIGNAL_APP_ID"); + OneSignal.initialize("YOUR_ONESIGNAL_APP_ID"); ``` If your integration is **not** user-centric, there is no additional startup code required. A device-scoped user *(please see definition of “**device-scoped user**” below in Glossary)* is automatically created as part of the push subscription creation, both of which are only accessible from the current device or through the OneSignal dashboard. @@ -130,39 +131,42 @@ Email and/or SMS subscriptions can be added or removed via the following methods # API Reference -Below is a comprehensive reference to the `5.0.0-beta-01` OneSignal React Native SDK. +Below is a comprehensive reference to the `5.0.0-beta-02` OneSignal React Native SDK. ## OneSignal The SDK is still accessible via a `OneSignal` static class. It provides access to higher level functionality and is a gateway to each subspace of the SDK. -| | **Description** | +|**React Native** | **Description** | |-----------------------------------------------------------------------------------------------| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `OneSignal.init("YOUR_ONESIGNAL_APP_ID")` | *Initializes the OneSignal SDK. This should be called during startup of the application.* | +| `OneSignal.initialize("YOUR_ONESIGNAL_APP_ID")` | *Initializes the OneSignal SDK. This should be called during startup of the application.* | | `OneSignal.User.login("USER_EXTERNAL_ID")` | *Login to OneSignal under the user identified by the [externalId] provided. The act of logging a user into the OneSignal SDK will switch the [user] context to that specific user.

- If the [externalId] exists, the user will be retrieved and the context will be set from that user information. If operations have already been performed under a device-scoped user, they ***will not*** be applied to the now logged in user (they will be lost).
- If the [externalId] does not exist the user, the user will be created and the context set from the current local state. If operations have already been performed under a device-scoped user, those operations ***will*** be applied to the newly created user.

***Push Notifications and In App Messaging***
Logging in a new user will automatically transfer the push notification and in app messaging subscription from the current user (if there is one) to the newly logged in user. This is because both push notifications and in-app messages are owned by the device.* | | `OneSignal.User.logout()` | *Logout the user previously logged in via [login]. The [user] property now references a new device-scoped user. A device-scoped user has no user identity that can later be retrieved, except through this device as long as the app remains installed and the app data is not cleared.* | -| `OneSignal.getPrivacyConsent()`

`OneSignal.setPrivacyConsent(true);` | *Indicates whether privacy consent has been granted. This field is only relevant when the application has opted into data privacy protections. See [requiresPrivacyConsent].* | -| `OneSignal.setRequiresPrivacyConsent(true)` | *Determines whether a user must consent to privacy prior to their user data being sent up to OneSignal. This should be set to `true` prior to the invocation of `initialize` to ensure compliance.* | +| `OneSignal.setConsentGiven(true)` | *Indicates whether privacy consent has been granted. This field is only relevant when the application has opted into data privacy protections. See [requiresPrivacyConsent].* | +| `OneSignal.setConsentRequired(true)` | *Determines whether a user must consent to privacy prior to their user data being sent up to OneSignal. This should be set to `true` prior to the invocation of `initialize` to ensure compliance.* | | `OneSignal.setLaunchURLsInApp(true)` | *This method can be used to set if launch URLs should be opened in safari or within the application. Set to `true` to launch all notifications with a URL in the app instead of the default web browser. Make sure to call `setLaunchURLsInApp` before the `initialize` call.* | -| `OneSignal.enterLiveActivity("ACTIVITY_ID", "TOKEN")`

***See below for usage of callbacks*** |*Entering a Live Activity associates an `activityId` with a live activity temporary push `token` on OneSignal's server. The activityId is then used with the OneSignal REST API to update one or multiple Live Activities at one time.* | -| `OneSignal.exitLiveActivity("ACTIVITY_ID")`

***See below for usage of callbacks*** |*Exiting a Live activity deletes the association between a customer defined `activityId` with a Live Activity temporary push `token` on OneSignal's server.* | -## Live Activities + +## Live Activities Namespace Live Activities are a type of interactive push notification. Apple introduced them in October 2022 to enable iOS apps to provide real-time updates to their users that are visible from the lock screen and the dynamic island. Please refer to OneSignal’s guide on [Live Activities](https://documentation.onesignal.com/docs/live-activities), the [Live Activities Quickstart](https://documentation.onesignal.com/docs/live-activities-quickstart) tutorial, and the [existing SDK reference](https://documentation.onesignal.com/docs/sdk-reference#live-activities) on Live Activities. +|**React Native** | **Description** | +| --------------------------------------------------------- | ---------------------------------------------------------------------------------------- | +| `OneSignal.LiveActivities.enter("ACTIVITY_ID", "TOKEN")`

***See below for usage of callbacks*** | *Entering a Live Activity associates an `activityId` with a live activity temporary push `token` on OneSignal's server. The activityId is then used with the OneSignal REST API to update one or multiple Live Activities at one time.* | +| `OneSignal.LiveActivities.exit("ACTIVITY_ID")`

***See below for usage of callbacks*** | *Exiting a Live activity deletes the association between a customer defined `activityId` with a Live Activity temporary push `token` on OneSignal's server.* | ```typescript // Enter a Live Activity - OneSignal.enterLiveActivity("ACTIVITY_ID", "TOKEN", (results) => { + OneSignal.LiveActivities.enter("ACTIVITY_ID", "TOKEN", (results) => { console.log("Results of entering live activity"); console.log(results); }); // Exit a Live Activity - OneSignal.exitLiveActivity("ACTIVITY_ID", (results) => { + OneSignal.LiveActivities.exit("ACTIVITY_ID", (results) => { console.log("Results of exiting live activity"); console.log(results); }); @@ -173,7 +177,7 @@ Please refer to OneSignal’s guide on [Live Activities](https://documentation.o The User namespace is accessible via `OneSignal.User` and provides access to user-scoped functionality. -| | **Description** | +|**React Native** | **Description** | |------------------------------------------------------------------------------------------| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `OneSignal.User.setLanguage("en")` | *Set the 2-character language for this user.* | | `OneSignal.User.addAlias("ALIAS_LABEL", "ALIAS_ID")` | *Set an alias for the current user. If this alias label already exists on this user, it will be overwritten with the new alias id.* | @@ -196,15 +200,15 @@ The User namespace is accessible via `OneSignal.User` and provides access to use The Push Subscription namespace is accessible via `OneSignal.User.pushSubscription` and provides access to push subscription-scoped functionality. -| | **Description** | +|**React Native** | **Description** | |------------------------------------------------------------------------------------------------------------------------------------------------| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `await OneSignal.User.PushSubscription.getId()` | *The readonly push subscription ID.* | | `await OneSignal.User.PushSubscription.getToken()` | *The readonly push token.* | | `await OneSignal.User.PushSubscription.getOptedIn()` | *Gets a boolean value indicating whether the current user is opted in to push notifications. This returns `true` when the app has notifications permission and `optedOut` is called. ***Note:*** Does not take into account the existence of the subscription ID and push token. This boolean may return `true` but push notifications may still not be received by the user.* | | `OneSignal.User.PushSubscription.optIn()` | *Call this method to receive push notifications on the device or to resume receiving of push notifications after calling `optOut`. If needed, this method will prompt the user for push notifications permission.* | | `OneSignal.User.PushSubscription.optOut()` | *If at any point you want the user to stop receiving push notifications on the current device (regardless of system-level permission status), you can call this method to opt out.* | -| `OneSignal.User.PushSubscription.addChangeHandler(handler: (event: ChangeEvent) => void)`

***See below for usage*** | *The `OSPushSubscriptionObserver.onOSPushSubscriptionChanged` method will be fired on the passed-in object when the push subscription changes. This method returns the current `OSPushSubscriptionState` at the time of adding this observer.* | -| `OneSignal.User.PushSubscription.removeChangeHandler()`

***See below for usage*** | *Remove a push subscription observer that has been previously added.* | +| `OneSignal.User.PushSubscription.addObserver(observer: (event) => void)`

***See below for usage*** | *The `OSPushSubscriptionObserver.onOSPushSubscriptionChanged` method will be fired on the passed-in object when the push subscription changes. This method returns the current `OSPushSubscriptionState` at the time of adding this observer.* | +| `OneSignal.User.PushSubscription.removeObserver(observer)`

***See below for usage*** | *Remove a push subscription observer that has been previously added.* | ### Push Subscription Observer @@ -213,12 +217,12 @@ Any object implementing the `OSPushSubscriptionObserver` protocol can be added a ```typescript // Create an observer -OneSignal.User.PushSubscription.addChangeHandler(subscription => { +OneSignal.User.PushSubscription.addObserver(subscription => { console.log('OneSignal: subscription changed: ', subscription); }); // Removes the previously added observer -OneSignal.User.PushSubscription.removeChangeHandler(); +OneSignal.User.PushSubscription.removeObserver(subscription); ``` ## Session Namespace @@ -226,7 +230,7 @@ OneSignal.User.PushSubscription.removeChangeHandler(); The Session namespace is accessible via `OneSignal.Session` and provides access to session-scoped functionality. -| | **Description** | +|**React Native** | **Description** | | --------------------------------------------------------- | ---------------------------------------------------------------------------------------- | | `OneSignal.Session.addOutcome("OUTCOME_NAME")` | *Add an outcome with the provided name, captured against the current session.* | | `OneSignal.Session.addUniqueOutcome("OUTCOME_NAME")` | *Add a unique outcome with the provided name, captured against the current session.* | @@ -238,10 +242,10 @@ The Session namespace is accessible via `OneSignal.Session` and provides access The Notifications namespace is accessible via `OneSignal.Notifications` and provides access to notification-scoped functionality. -| | **Description** | +|**React Native** | **Description** | |----------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------| | `await OneSignal.Notifications.hasPermission()` | *Whether this app has push notification permission.* | -| `await OneSignal.Notifications.canRequestPermission()` | *Whether attempting to request notification permission will show a prompt. Returns `true` if the device has not been prompted for push notification permission already.* | +| `await OneSignal.Notifications.canRequestPermission()` | *Whether attempting to request notification permission will show a prompt. Returns `true` if the device has not been prompted for push notification permission already.* | `await OneSignal.Notifications.permissionNative()` | *(ios only) Returns the enum for the native permission of the device. It will be one of: NotDetermined, Denied, Authorized, Provisional (only available in iOS 12), Ephemeral (only available in iOS 14) * | | `OneSignal.Notifications.clearAll();` | *Removes all OneSignal notifications.*| | `OneSignal.Notifications.removeNotification("NOTIFICATION_ID")` | *(Android only) Cancels a single OneSignal notification based on its Android notification integer ID. Use instead of Android's [android.app.NotificationManager.cancel], otherwise the notification will be restored when your app is restarted.*| | `OneSignal.Notifications.removeGroupedNotifications("GROUP_KEY")` | *(Android only) Cancels a group of OneSignal notifications with the provided group key. Grouping notifications is a OneSignal concept, there is no [android.app.NotificationManager] equivalent.*| @@ -249,8 +253,8 @@ The Notifications namespace is accessible via `OneSignal.Notifications` and prov | `OneSignal.Notifications.registerForProvisionalAuthorization()` | *(iOS only) Instead of having to prompt the user for permission to send them push notifications, your app can request provisional authorization.*| | `OneSignal.Notifications.addPermissionObserver(observer)`

***See below for usage*** | *This method will fire when a notification permission setting changes. This happens when the user enables or disables notifications for your app from the system settings outside of your app.*| | `OneSignal.Notifications.removePermissionObserver(observer)`

***See below for usage*** | *Remove a push permission observer that has been previously added.*| -| `OneSignal.Notifications.setNotificationWillShowInForegroundHandler(handler)`

***See below for usage*** | *Sets the handler to run before displaying a notification while the app is in focus. Use this handler to read notification data and change it or decide if the notification ***should*** show or not.

***Note:*** this runs ***after*** the [Notification Service Extension](https://documentation.onesignal.com/docs/service-extensions) which can be used to modify the notification before showing it.* | -| `OneSignal.Notifications.setNotificationOpenedHandler()`

***See below for usage*** | *Sets a handler that will run whenever a notification is opened by the user.*| +| `OneSignal.Notifications.addEventListener("foregroundWillDisplay", (event) => {};)`

***See below for usage*** | *Sets the handler to run before displaying a notification while the app is in focus. Use this handler to read notification data and change it or decide if the notification ***should*** show or not.

***Note:*** this runs ***after*** the [Notification Service Extension](https://documentation.onesignal.com/docs/service-extensions) which can be used to modify the notification before showing it.* | +| `OneSignal.Notifications.addEventListener("click", (event) => {};)`

***See below for usage*** | *Sets a handler that will run whenever a notification is opened by the user.*| ### Prompt for Push Notification Permission @@ -261,37 +265,41 @@ OneSignal.Notifications.requestPermission(accepted => { ``` ### Permission Observer -Add an observer when permission status changes. You can call `removePermissionChangedHandler` to remove any existing listeners. +Add an observer when permission status changes. You can call `removePermissionObserver` to remove any existing listeners. ```typescript -// Add a change hanlder -OneSignal.Notifications.addPermissionChangedHandler(granted => { - console.log("OneSignal: permission state changed to: ", granted); -}); - -// Remove previously added handler -OneSignal.Notifications.removePermissionChangedHandler(); +// Add an observer +let observer = function(req) { + console.log("OneSignal: permission state changed to: " + (req)); +}; +OneSignal.Notifications.addPermissionObserver(observer); + +// Remove previously added observer +OneSignal.Notifications.removePermissionObserver(observer); ``` -### Notification Will Show in Foreground Handler +### Notification Lifecycle Listener ```typescript -OneSignal.Notifications.setNotificationWillShowInForegroundHandler(notificationReceivedEvent => { - notificationReceivedEvent.complete(notificationReceivedEvent.getNotification()); +OneSignal.Notifications.addEventListener("foregroundWillDisplay", (event) => { + event.preventDefault(); + // some async work + + // Use display() to display the notification after some async work + event.getNotification().display(); }); ``` -### Notification Click Handler +### Notification Click Listener ```typescript -OneSignal.Notifications.setNotificationClickHandler(clicked => { - const notificationData = JSON.stringify(clicked); - console.log('notificationOpenedCallback: ' + notificationData); +OneSignal.Notifications.addEventListener("click", (event) => { + console.log('OneSignal: notification clicked: ' + event); }); ``` ## Location Namespace The Location namespace is accessible via `OneSignal.Location` and provide access to location-scoped functionality. -| | **Description** | +|**React Native** | **Description** | |-------------------------------------------------------------------------|-------------------------------------------------------------------------------------------| | `await OneSignal.Location.isShared()`

***See below for usage*** | *Whether location is currently shared with OneSignal.*| | `OneSignal.Location.requestPermission()` | *Use this method to manually prompt the user for location permissions. This allows for geotagging so you send notifications to users based on location.* | @@ -322,36 +330,36 @@ const paused = await OneSignal.InAppMessages.getPaused(); console.log("IAM paused: ", paused); ``` -### In-App Message Click Handler +### In-App Message Click Listener ```typescript -OneSignal.InAppMessages.setClickHandler(result => { - const iamClickAction = JSON.stringify(result); - console.log('iamClickCallback: ' + iamClickAction); +OneSignal.InAppMessages.addEventListener("click", (event) => { + console.log('OneSignal IAM clicked: ' + event); }); ``` -### In-App Message Lifecycle Handler +### In-App Message Lifecycle Listeners ```typescript - OneSignal.InAppMessages.setLifecycleHandler({ - onWillDisplayInAppMessage: message => { - console.log("OneSignal: will display IAM: ", message.messageId) - }, - onDidDisplayInAppMessage: message => { - console.log("OneSignal: did display IAM: ", message.messageId) - }, - onWillDismissInAppMessage: message => { - console.log("OneSignal: will dismiss IAM: ", message.messageId) - }, - onDidDismissInAppMessage: message => { - console.log("OneSignal: did dismiss IAM: ", message.messageId) - } - }); +OneSignal.InAppMessages.addEventListener('willDisplay', (event) =>{ + console.log('OneSignal: will display IAM: ', event); +}); + +OneSignal.InAppMessages.addEventListener('didDisplay', (event) =>{ + console.log('OneSignal: did display IAM: ', event); +}); + +OneSignal.InAppMessages.addEventListener('willDismiss', (event) =>{ + console.log('OneSignal: will dismiss IAM: ', event); +}); + +OneSignal.InAppMessages.addEventListener('didDismiss', (event) =>{ + console.log('OneSignal: did dismiss IAM: ', event); +}); ``` ## Debug Namespace The Debug namespace is accessible via `OneSignal.Debug` and provide access to debug-scoped functionality. -| **Objective-C** | **Description** | +| **React Native** | **Description** | | ---------------------------------------------- | ---------------------------------------------------------------------------------- | | `OneSignal.Debug.setLogLevel(6)` | *Sets the log level the OneSignal SDK should be writing to the Xcode log.* | | `OneSignal.Debug.setAlertLevel(0)` | *Sets the logging level to show as alert dialogs.* | @@ -364,11 +372,12 @@ The Debug namespace is accessible via `OneSignal.Debug` and provide access to de # Limitations **General** - Recommend using only in development and staging environments for Alpha releases +- Aliases will be available in a future release +- Outcomes will be available in a future release - Users are deleted when the last Subscription (push, email, or sms) is removed - Any `User` namespace calls must be invoked **after** initialization. Example: `OneSignal.User.addTag("tag", "2")` # Known issues - Identity Verification - We will be introducing JWT in a follow-up Beta release -- User properties may not update correctly when Subscriptions are transferred - - Please report any issues you find with this \ No newline at end of file + \ No newline at end of file diff --git a/android/.gradle/4.8/fileChanges/last-build.bin b/android/.gradle/4.8/fileChanges/last-build.bin new file mode 100644 index 00000000..f76dd238 Binary files /dev/null and b/android/.gradle/4.8/fileChanges/last-build.bin differ diff --git a/android/.gradle/4.8/fileHashes/fileHashes.bin b/android/.gradle/4.8/fileHashes/fileHashes.bin new file mode 100644 index 00000000..3b4fa35d Binary files /dev/null and b/android/.gradle/4.8/fileHashes/fileHashes.bin differ diff --git a/android/.gradle/4.8/fileHashes/fileHashes.lock b/android/.gradle/4.8/fileHashes/fileHashes.lock new file mode 100644 index 00000000..3987d30b Binary files /dev/null and b/android/.gradle/4.8/fileHashes/fileHashes.lock differ diff --git a/android/.gradle/vcsWorkingDirs/gc.properties b/android/.gradle/vcsWorkingDirs/gc.properties new file mode 100644 index 00000000..e69de29b diff --git a/android/.idea/.gitignore b/android/.idea/.gitignore new file mode 100644 index 00000000..26d33521 --- /dev/null +++ b/android/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/android/.idea/gradle.xml b/android/.idea/gradle.xml new file mode 100644 index 00000000..f22a5399 --- /dev/null +++ b/android/.idea/gradle.xml @@ -0,0 +1,13 @@ + + + + + + + \ No newline at end of file diff --git a/android/build.gradle b/android/build.gradle index 5bbc78a9..1b94c8a3 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -31,7 +31,7 @@ dependencies { // api is used instead of implementation so the parent :app project can access any of the OneSignal Java // classes if needed. Such as com.onesignal.NotificationExtenderService - api 'com.onesignal:OneSignal:5.0.0-beta2' + api 'com.onesignal:OneSignal:5.0.0-beta4' testImplementation 'junit:junit:4.12' } \ No newline at end of file diff --git a/android/src/main/java/com/geektime/rnonesignalandroid/RNOneSignal.java b/android/src/main/java/com/geektime/rnonesignalandroid/RNOneSignal.java index 34fe7af2..712e28f0 100644 --- a/android/src/main/java/com/geektime/rnonesignalandroid/RNOneSignal.java +++ b/android/src/main/java/com/geektime/rnonesignalandroid/RNOneSignal.java @@ -53,28 +53,34 @@ of this software and associated documentation files (the "Software"), to deal import com.onesignal.OneSignal; import com.onesignal.debug.LogLevel; import com.onesignal.inAppMessages.IInAppMessage; -import com.onesignal.inAppMessages.IInAppMessageClickHandler; +import com.onesignal.inAppMessages.IInAppMessageClickListener; +import com.onesignal.inAppMessages.IInAppMessageClickEvent; import com.onesignal.inAppMessages.IInAppMessageClickResult; -import com.onesignal.inAppMessages.IInAppMessageLifecycleHandler; +import com.onesignal.inAppMessages.IInAppMessageLifecycleListener; +import com.onesignal.inAppMessages.IInAppMessageWillDisplayEvent; +import com.onesignal.inAppMessages.IInAppMessageDidDisplayEvent; +import com.onesignal.inAppMessages.IInAppMessageWillDismissEvent; +import com.onesignal.inAppMessages.IInAppMessageDidDismissEvent; import com.onesignal.notifications.INotification; -import com.onesignal.notifications.INotificationClickHandler; -import com.onesignal.notifications.INotificationClickResult; -import com.onesignal.notifications.INotificationReceivedEvent; -import com.onesignal.notifications.INotificationWillShowInForegroundHandler; -import com.onesignal.notifications.IPermissionChangedHandler; +import com.onesignal.notifications.INotificationClickListener; +import com.onesignal.notifications.INotificationClickEvent; +import com.onesignal.notifications.INotificationLifecycleListener; +import com.onesignal.notifications.INotificationWillDisplayEvent; +import com.onesignal.notifications.IPermissionObserver; import com.onesignal.user.subscriptions.IPushSubscription; -import com.onesignal.user.subscriptions.ISubscription; -import com.onesignal.user.subscriptions.ISubscriptionChangedHandler; +import com.onesignal.user.subscriptions.IPushSubscriptionObserver; +import com.onesignal.user.subscriptions.PushSubscriptionState; +import com.onesignal.user.subscriptions.PushSubscriptionChangedState; import org.json.JSONException; import org.json.JSONObject; import java.util.HashMap; public class RNOneSignal extends ReactContextBaseJavaModule implements - ISubscriptionChangedHandler, - IPermissionChangedHandler, + IPushSubscriptionObserver, + IPermissionObserver, LifecycleEventListener, - INotificationWillShowInForegroundHandler{ + INotificationLifecycleListener{ private ReactApplicationContext mReactApplicationContext; private ReactContext mReactContext; @@ -82,27 +88,91 @@ public class RNOneSignal extends ReactContextBaseJavaModule implements private boolean hasSetPermissionObserver = false; private boolean hasSetPushSubscriptionObserver = false; - private HashMap notificationReceivedEventCache; - private boolean hasSetNotificationWillShowInForegroundHandler = false; + private HashMap notificationWillDisplayCache; + private HashMap preventDefaultCache; + + private boolean hasAddedNotificationForegroundListener = false; + private boolean hasAddedInAppMessageLifecycleListener = false; + + private IInAppMessageClickListener rnInAppClickListener = new IInAppMessageClickListener() { + @Override + public void onClick(IInAppMessageClickEvent event) { + try { + IInAppMessageClickResult result = event.getResult(); + sendEvent("OneSignal-inAppMessageClicked", RNUtils.convertHashMapToWritableMap(RNUtils.convertInAppMessageClickResultToMap(result))); + } catch (JSONException e) { + e.printStackTrace(); + } + } + }; + + private IInAppMessageLifecycleListener rnInAppLifecycleListener = new IInAppMessageLifecycleListener() { + @Override + public void onWillDisplay(IInAppMessageWillDisplayEvent event) { + try { + sendEvent("OneSignal-inAppMessageWillDisplay", RNUtils.convertHashMapToWritableMap(RNUtils.convertInAppMessageToMap(event.getMessage()))); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + @Override + public void onDidDisplay(IInAppMessageDidDisplayEvent event) { + try { + sendEvent("OneSignal-inAppMessageDidDisplay", RNUtils.convertHashMapToWritableMap(RNUtils.convertInAppMessageToMap(event.getMessage()))); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + @Override + public void onWillDismiss(IInAppMessageWillDismissEvent event) { + try { + sendEvent("OneSignal-inAppMessageWillDismiss", RNUtils.convertHashMapToWritableMap(RNUtils.convertInAppMessageToMap(event.getMessage()))); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + @Override + public void onDidDismiss(IInAppMessageDidDismissEvent event) { + try { + sendEvent("OneSignal-inAppMessageDidDismiss", RNUtils.convertHashMapToWritableMap(RNUtils.convertInAppMessageToMap(event.getMessage()))); + } catch (JSONException e) { + e.printStackTrace(); + } + } + }; + + private INotificationClickListener rnNotificationClickListener = new INotificationClickListener() { + @Override + public void onClick(INotificationClickEvent event) { + try { + sendEvent("OneSignal-notificationClicked", RNUtils.convertHashMapToWritableMap(RNUtils.convertNotificationToMap(event.getNotification()))); + } catch (JSONException e) { + e.printStackTrace(); + } + } + }; private void removeObservers() { - this.removePermissionChangedHandler(); - this.rmeovePushSubscriptionChangeHandler(); + this.removePermissionObserver(); + this.removePushSubscriptionObserver(); } private void removeHandlers() { - OneSignal.getInAppMessages().setInAppMessageClickHandler(null); - OneSignal.getInAppMessages().setInAppMessageLifecycleHandler(null); - OneSignal.getNotifications().setNotificationClickHandler(null); - OneSignal.getNotifications().setNotificationWillShowInForegroundHandler(null); + OneSignal.getInAppMessages().removeClickListener(rnInAppClickListener); + OneSignal.getInAppMessages().removeLifecycleListener(rnInAppLifecycleListener); + OneSignal.getNotifications().removeClickListener(rnNotificationClickListener); + OneSignal.getNotifications().removeForegroundLifecycleListener(this); } private void sendEvent(String eventName, Object params) { mReactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(eventName, params); } - private void initNotificationWillShowInForegroundHandlerParams() { - this.hasSetNotificationWillShowInForegroundHandler = true; + private void initNotificationWillDisplayInForegroundListenerParams() { + this.hasAddedNotificationForegroundListener = true; } public RNOneSignal(ReactApplicationContext reactContext) { @@ -110,7 +180,8 @@ public RNOneSignal(ReactApplicationContext reactContext) { mReactApplicationContext = reactContext; mReactContext = reactContext; mReactContext.addLifecycleEventListener(this); - notificationReceivedEventCache = new HashMap(); + notificationWillDisplayCache = new HashMap(); + preventDefaultCache = new HashMap(); } @@ -160,23 +231,13 @@ public void initialize(String appId) { } @ReactMethod - public void setPrivacyConsent(Boolean value) { - OneSignal.setPrivacyConsent(value); + public void setPrivacyConsentGiven(Boolean value) { + OneSignal.setConsentGiven(value); } @ReactMethod - public void getPrivacyConsent(Promise promise) { - promise.resolve(OneSignal.getPrivacyConsent()); - } - - @ReactMethod - public void setRequiresPrivacyConsent(Boolean required) { - OneSignal.setRequiresPrivacyConsent(required); - } - - @ReactMethod - public void getRequiresPrivacyConsent(Promise promise) { - promise.resolve(OneSignal.getRequiresPrivacyConsent()); + public void setPrivacyConsentRequired(Boolean required) { + OneSignal.setConsentRequired(required); } @@ -194,58 +255,16 @@ public void setAlertLevel(int logLevel) { // OneSignal.InAppMessages namespace methods @ReactMethod - public void setInAppMessageClickHandler() { - OneSignal.getInAppMessages().setInAppMessageClickHandler(new IInAppMessageClickHandler() { - @Override - public void inAppMessageClicked(IInAppMessageClickResult result) { - try { - sendEvent("OneSignal-inAppMessageClicked", RNUtils.convertHashMapToWritableMap(RNUtils.convertInAppMessageClickedActionToMap(result))); - } catch (JSONException e) { - e.printStackTrace(); - } - } - }); + public void addInAppMessageClickListener() { + OneSignal.getInAppMessages().addClickListener(rnInAppClickListener); } @ReactMethod - public void setInAppMessagesLifecycleHandler() { - OneSignal.getInAppMessages().setInAppMessageLifecycleHandler(new IInAppMessageLifecycleHandler() { - @Override - public void onWillDisplayInAppMessage(IInAppMessage message) { - try { - sendEvent("OneSignal-inAppMessageWillDisplay", RNUtils.convertHashMapToWritableMap(RNUtils.convertInAppMessageToMap(message))); - } catch (JSONException e) { - e.printStackTrace(); - } - } - - @Override - public void onDidDisplayInAppMessage(IInAppMessage message) { - try { - sendEvent("OneSignal-inAppMessageDidDisplay", RNUtils.convertHashMapToWritableMap(RNUtils.convertInAppMessageToMap(message))); - } catch (JSONException e) { - e.printStackTrace(); - } - } - - @Override - public void onWillDismissInAppMessage(IInAppMessage message) { - try { - sendEvent("OneSignal-inAppMessageWillDismiss", RNUtils.convertHashMapToWritableMap(RNUtils.convertInAppMessageToMap(message))); - } catch (JSONException e) { - e.printStackTrace(); - } - } - - @Override - public void onDidDismissInAppMessage(IInAppMessage message) { - try { - sendEvent("OneSignal-inAppMessageDidDismiss", RNUtils.convertHashMapToWritableMap(RNUtils.convertInAppMessageToMap(message))); - } catch (JSONException e) { - e.printStackTrace(); - } - } - }); + public void addInAppMessagesLifecycleListener() { + if (!hasAddedInAppMessageLifecycleListener) { + OneSignal.getInAppMessages().addLifecycleListener(rnInAppLifecycleListener); + hasAddedInAppMessageLifecycleListener = true; + } } @ReactMethod @@ -265,7 +284,7 @@ public void addTrigger(String key, String value) { @ReactMethod public void addTriggers(ReadableMap triggers) { - OneSignal.getInAppMessages().addTriggers(triggers.toHashMap()); + OneSignal.getInAppMessages().addTriggers(RNUtils.convertReadableMapIntoStringMap(triggers)); } @ReactMethod @@ -275,7 +294,7 @@ public void removeTrigger(String key) { @ReactMethod public void removeTriggers(ReadableArray keys) { - OneSignal.getInAppMessages().removeTriggers(RNUtils.convertReableArrayIntoStringCollection(keys)); + OneSignal.getInAppMessages().removeTriggers(RNUtils.convertReadableArrayIntoStringCollection(keys)); } @ReactMethod @@ -303,49 +322,40 @@ public void setLocationShared(Boolean shared) { // OneSignal.Notifications namespace methods @ReactMethod - public void setNotificationClickHandler() { - OneSignal.getNotifications().setNotificationClickHandler(new INotificationClickHandler() { - @Override - public void notificationClicked(INotificationClickResult result) { - try { - sendEvent("OneSignal-notificationClicked", RNUtils.convertHashMapToWritableMap(RNUtils.convertNotificationClickResultToMap(result))); - } catch (JSONException e) { - e.printStackTrace(); - } - } - }); + public void addNotificationClickListener() { + OneSignal.getNotifications().addClickListener(rnNotificationClickListener); } @ReactMethod - public void setNotificationWillShowInForegroundHandler() { - if (this.hasSetNotificationWillShowInForegroundHandler) { + public void addNotificationForegroundLifecycleListener() { + if (this.hasAddedNotificationForegroundListener) { return; } - OneSignal.getNotifications().setNotificationWillShowInForegroundHandler(this); - hasSetNotificationWillShowInForegroundHandler = true; + OneSignal.getNotifications().addForegroundLifecycleListener(this); + hasAddedNotificationForegroundListener = true; } @Override - public void notificationWillShowInForeground(INotificationReceivedEvent notificationReceivedEvent) { - if (!this.hasSetNotificationWillShowInForegroundHandler) { - notificationReceivedEvent.complete(notificationReceivedEvent.getNotification()); - return; + public void onWillDisplay(INotificationWillDisplayEvent event) { + if (!this.hasAddedNotificationForegroundListener) { + event.getNotification().display(); } - INotification notification = notificationReceivedEvent.getNotification(); + INotification notification = event.getNotification(); String notificationId = notification.getNotificationId(); - notificationReceivedEventCache.put(notificationId, notificationReceivedEvent); + notificationWillDisplayCache.put(notificationId, (INotificationWillDisplayEvent) event); + event.preventDefault(); try { - sendEvent("OneSignal-notificationWillShowInForeground", + sendEvent("OneSignal-notificationWillDisplayInForeground", RNUtils.convertHashMapToWritableMap( RNUtils.convertNotificationToMap(notification))); try { - synchronized (notificationReceivedEvent) { - while (notificationReceivedEventCache.containsKey(notificationId)) { - notificationReceivedEvent.wait(); + synchronized (event) { + while (preventDefaultCache.containsKey(notificationId)) { + event.wait(); } } } catch(InterruptedException e){ @@ -357,41 +367,44 @@ public void notificationWillShowInForeground(INotificationReceivedEvent notifica } @ReactMethod - public void completeNotificationEvent(final String uuid, final boolean shouldDisplay) { - INotificationReceivedEvent receivedEvent = notificationReceivedEventCache.get(uuid); - - if (receivedEvent == null) { - Log.e("OneSignal", "(java): could not find cached notification received event with id "+uuid); + private void displayNotification(String notificationId){ + INotificationWillDisplayEvent event = notificationWillDisplayCache.get(notificationId); + if (event == null) { + Log.e("Could not find onWillDisplayNotification event for notification with id: " + notificationId, null); return; } + event.getNotification().display(); + } - if (shouldDisplay) { - receivedEvent.complete(receivedEvent.getNotification()); - } else { - receivedEvent.complete(null); + @ReactMethod + private void preventDefault(String notificationId) { + INotificationWillDisplayEvent event = notificationWillDisplayCache.get(notificationId); + if (event == null) { + Log.e("Could not find onWillDisplayNotification event for notification with id: " + notificationId, null); + return; } - - notificationReceivedEventCache.remove(uuid); + event.preventDefault(); + this.preventDefaultCache.put(notificationId, event); } @ReactMethod - public void addPermissionChangedHandler() { + public void addPermissionObserver() { if (!hasSetPermissionObserver) { - OneSignal.getNotifications().addPermissionChangedHandler(this); + OneSignal.getNotifications().addPermissionObserver(this); hasSetPermissionObserver = true; } } @ReactMethod - public void removePermissionChangedHandler() { + public void removePermissionObserver() { if (hasSetPermissionObserver) { - OneSignal.getNotifications().removePermissionChangedHandler(this); + OneSignal.getNotifications().removePermissionObserver(this); hasSetPermissionObserver = false; } } @Override - public void onPermissionChanged(boolean permission) { + public void onNotificationPermissionChange(boolean permission) { try { sendEvent("OneSignal-permissionChanged", RNUtils.convertHashMapToWritableMap(RNUtils.convertPermissionToMap(permission))); Log.i("OneSignal", "sending permission change event"); @@ -465,37 +478,37 @@ public void optOut() { } @ReactMethod - public void addPushSubscriptionChangeHandler() { + public void addPushSubscriptionObserver() { if (!hasSetPushSubscriptionObserver) { - OneSignal.getUser().getPushSubscription().addChangeHandler(this); + OneSignal.getUser().getPushSubscription().addObserver(this); hasSetPushSubscriptionObserver = true; } } - @ReactMethod - public void rmeovePushSubscriptionChangeHandler() { - if (hasSetPushSubscriptionObserver) { - OneSignal.getUser().getPushSubscription().removeChangeHandler(this); - hasSetPushSubscriptionObserver = false; - } - } - @Override - public void onSubscriptionChanged(ISubscription subscription) { - if (!(subscription instanceof IPushSubscription)) { + public void onPushSubscriptionChange(PushSubscriptionChangedState pushSubscriptionChangedState) { + PushSubscriptionState pushSubscription = pushSubscriptionChangedState.getCurrent(); + if (!(pushSubscription instanceof PushSubscriptionState)){ return; } try { sendEvent("OneSignal-subscriptionChanged", RNUtils.convertHashMapToWritableMap( - RNUtils.convertOnSubscriptionChangedToMap((IPushSubscription) subscription))); + RNUtils.convertOnSubscriptionChangedToMap(pushSubscription))); Log.i("OneSignal", "sending subscription change event"); } catch (JSONException e) { e.printStackTrace(); - } + } } + @ReactMethod + public void removePushSubscriptionObserver() { + if (hasSetPushSubscriptionObserver) { + OneSignal.getUser().getPushSubscription().removeObserver(this); + hasSetPushSubscriptionObserver = false; + } + } // OneSignal.Session namespace methods @ReactMethod @@ -541,12 +554,12 @@ public void removeTag(String key) { @ReactMethod public void addTags(ReadableMap tags) { - OneSignal.getUser().addTags(RNUtils.convertReableMapIntoStringMap(tags)); + OneSignal.getUser().addTags(RNUtils.convertReadableMapIntoStringMap(tags)); } @ReactMethod public void removeTags(ReadableArray tagKeys) { - OneSignal.getUser().removeTags(RNUtils.convertReableArrayIntoStringCollection(tagKeys)); + OneSignal.getUser().removeTags(RNUtils.convertReadableArrayIntoStringCollection(tagKeys)); } @ReactMethod @@ -601,12 +614,12 @@ public void removeAlias(String label) { @ReactMethod public void addAliases(ReadableMap aliases) { - OneSignal.getUser().addAliases(RNUtils.convertReableMapIntoStringMap(aliases)); + OneSignal.getUser().addAliases(RNUtils.convertReadableMapIntoStringMap(aliases)); } @ReactMethod public void removeAliases(ReadableArray aliasLabels) { - OneSignal.getUser().removeAliases(RNUtils.convertReableArrayIntoStringCollection(aliasLabels)); + OneSignal.getUser().removeAliases(RNUtils.convertReadableArrayIntoStringCollection(aliasLabels)); } /** Added for NativeEventEmitter */ diff --git a/android/src/main/java/com/geektime/rnonesignalandroid/RNUtils.java b/android/src/main/java/com/geektime/rnonesignalandroid/RNUtils.java index 50b1b8b4..eeac44fb 100644 --- a/android/src/main/java/com/geektime/rnonesignalandroid/RNUtils.java +++ b/android/src/main/java/com/geektime/rnonesignalandroid/RNUtils.java @@ -12,15 +12,17 @@ import com.facebook.react.bridge.WritableNativeArray; import com.facebook.react.bridge.WritableNativeMap; -// import com.onesignal.OSInAppMessage; import com.onesignal.inAppMessages.IInAppMessage; import com.onesignal.inAppMessages.IInAppMessageClickResult; +import com.onesignal.inAppMessages.IInAppMessageWillDisplayEvent; +import com.onesignal.inAppMessages.IInAppMessageDidDisplayEvent; +import com.onesignal.inAppMessages.IInAppMessageWillDismissEvent; +import com.onesignal.inAppMessages.IInAppMessageDidDismissEvent; import com.onesignal.notifications.INotification; -import com.onesignal.notifications.INotificationAction; import com.onesignal.notifications.INotificationClickResult; import com.onesignal.notifications.INotificationReceivedEvent; import com.onesignal.user.subscriptions.IPushSubscription; -import com.onesignal.user.subscriptions.ISubscription; +import com.onesignal.user.subscriptions.PushSubscriptionState; import org.json.JSONArray; import org.json.JSONException; @@ -109,15 +111,6 @@ public static HashMap convertNotificationToMap(INotification not return hash; } - public static HashMap convertNotificationClickResultToMap(INotificationClickResult openResult) throws JSONException { - HashMap hash = new HashMap<>(); - - hash.put("notification", convertNotificationToMap(openResult.getNotification())); - hash.put("action", convertNotificationActionToMap(openResult.getAction())); - - return hash; - } - public static HashMap convertInAppMessageToMap(IInAppMessage message) { HashMap hash = new HashMap<>(); @@ -126,18 +119,18 @@ public static HashMap convertInAppMessageToMap(IInAppMessage mes return hash; } - public static HashMap convertInAppMessageClickedActionToMap(IInAppMessageClickResult result) { + public static HashMap convertInAppMessageClickResultToMap(IInAppMessageClickResult result) { HashMap hash = new HashMap<>(); - hash.put("clickName", result.getAction().getClickName()); - hash.put("clickUrl", result.getAction().getClickUrl()); - hash.put("firstClick", result.getAction().isFirstClick()); - hash.put("closesMessage", result.getAction().getClosesMessage()); + hash.put("actionId", result.getActionId()); + hash.put("urlTarget", result.getUrlTarget()); + hash.put("url", result.getUrl()); + hash.put("closingMessage", result.getClosingMessage()); return hash; } - public static HashMap convertOnSubscriptionChangedToMap(IPushSubscription state) { + public static HashMap convertOnSubscriptionChangedToMap(PushSubscriptionState state) { HashMap hash = new HashMap<>(); hash.put("token", state.getToken()); @@ -175,7 +168,7 @@ public static HashMap convertJSONObjectToHashMap(JSONObject obje return hash; } - public static Collection convertReableArrayIntoStringCollection(ReadableArray readableArray) { + public static Collection convertReadableArrayIntoStringCollection(ReadableArray readableArray) { ArrayList strings = new ArrayList<>(); for (Object object : readableArray.toArrayList()) { if (object instanceof String) @@ -184,7 +177,7 @@ public static Collection convertReableArrayIntoStringCollection(Readable return strings; } - public static HashMap convertReableMapIntoStringMap(ReadableMap readableMap) { + public static HashMap convertReadableMapIntoStringMap(ReadableMap readableMap) { HashMap stringMap = new HashMap<>(); ReadableMapKeySetIterator iter = readableMap.keySetIterator(); @@ -206,22 +199,6 @@ public static HashMap convertPermissionToMap(boolean granted) { return hash; } - private static HashMap convertNotificationActionToMap(INotificationAction action) { - HashMap hash = new HashMap<>(); - - hash.put("id", action.getActionId()); - - switch (action.getType()) { - case Opened: - hash.put("type", 0); - break; - case ActionTaken: - hash.put("type", 1); - } - - return hash; - } - private static List convertJSONArrayToList(JSONArray array) throws JSONException { List list = new ArrayList<>(); diff --git a/examples/RNOneSignalTS/src/OSButtons.tsx b/examples/RNOneSignalTS/src/OSButtons.tsx index c4ea80da..6ac0b34b 100644 --- a/examples/RNOneSignalTS/src/OSButtons.tsx +++ b/examples/RNOneSignalTS/src/OSButtons.tsx @@ -420,61 +420,43 @@ class OSButtons extends React.Component { privacyConsentFields() { const {loggingFunction} = this.props; - const getPrivacyConsentButton = renderButtonView( - 'Get Privacy Consent', - async () => { - const granted = await OneSignal.getPrivacyConsent(); - loggingFunction('Privacy consent granted: ', granted); - } - ); - - const setPrivacyConsentTrueButton = renderButtonView( + const setPrivacyConsentGivenTrueButton = renderButtonView( 'Set Privacy Consent to true', async () => { - await OneSignal.setPrivacyConsent(true); + await OneSignal.setConsentGiven(true); loggingFunction('Privacy Consent set to true'); } ); - const setPrivacyConsentFalseButton = renderButtonView( + const setPrivacyConsentGivenFalseButton = renderButtonView( 'Set Privacy Consent to false', async () => { - await OneSignal.setPrivacyConsent(false); + await OneSignal.setConsentGiven(false); loggingFunction('Privacy Consent set to false'); } ); - const getRequiresPrivacyConsentButton = renderButtonView( - 'Get Requires Privacy Consent', - async () => { - const granted = await OneSignal.getRequiresPrivacyConsent(); - loggingFunction('Requires Privacy Consent: ', granted); - } - ); - - const setRequiresPrivacyConsentTrueButton = renderButtonView( + const setPrivacyConsentRequiredTrueButton = renderButtonView( 'Set Requiers Privacy Consent to true', async () => { - await OneSignal.setRequiresPrivacyConsent(true); + await OneSignal.setConsentRequired(true); loggingFunction('Requires Privacy Consent set to true'); } ); - const setRequiresPrivacyConsentFalseButton = renderButtonView( + const setPrivacyConsentRequiredFalseButton = renderButtonView( 'Set Requiers Privacy Consent to false', async () => { - await OneSignal.setRequiresPrivacyConsent(false); + await OneSignal.setConsentRequired(false); loggingFunction('Requires Privacy Consent set to false'); } ); return [ - getPrivacyConsentButton, - setPrivacyConsentTrueButton, - setPrivacyConsentFalseButton, - getRequiresPrivacyConsentButton, - setRequiresPrivacyConsentTrueButton, - setRequiresPrivacyConsentFalseButton, + setPrivacyConsentGivenTrueButton, + setPrivacyConsentGivenFalseButton, + setPrivacyConsentRequiredTrueButton, + setPrivacyConsentRequiredFalseButton, ]; } diff --git a/examples/RNOneSignalTS/src/OSDemo.tsx b/examples/RNOneSignalTS/src/OSDemo.tsx index a4764030..bd339f52 100644 --- a/examples/RNOneSignalTS/src/OSDemo.tsx +++ b/examples/RNOneSignalTS/src/OSDemo.tsx @@ -33,60 +33,61 @@ class OSDemo extends React.Component { OneSignal.initialize(APP_ID); OneSignal.Debug.setLogLevel(6); - OneSignal.Notifications.setNotificationWillShowInForegroundHandler( - (notifReceivedEvent) => { - this.OSLog('OneSignal: notification will show in foreground:', notifReceivedEvent); - const notification = notifReceivedEvent.getNotification(); + OneSignal.Notifications.addEventListener('foregroundWillDisplay', + (event) => { + this.OSLog('OneSignal: notification will show in foreground:', event); + let notif = event.getNotification(); const cancelButton = { text: 'Cancel', onPress: () => { - notifReceivedEvent.complete(); + event.preventDefault(); }, style: 'cancel', }; const completeButton = { - text: 'Complete', + text: 'Display', onPress: () => { - notifReceivedEvent.complete(notification); + event.getNotification().display(); }, }; - Alert.alert('Complete notification?', notification.title, [cancelButton, completeButton], { + Alert.alert('Display notification?', notif.title, [cancelButton, completeButton], { cancelable: true, }); }, ); - OneSignal.Notifications.setNotificationClickHandler((notification) => { - this.OSLog('OneSignal: notification opened:', notification); + OneSignal.Notifications.addEventListener('click', (event) => { + this.OSLog('OneSignal: notification clicked:', event); }); - OneSignal.InAppMessages.setClickHandler((event) => { + OneSignal.InAppMessages.addEventListener('click', (event) =>{ this.OSLog('OneSignal IAM clicked:', event); }); - OneSignal.InAppMessages.setLifecycleHandler({ - onWillDisplayInAppMessage: (message) => { - this.OSLog('OneSignal: will display IAM: ', message.messageId); - }, - onDidDisplayInAppMessage: (message) => { - this.OSLog('OneSignal: did display IAM: ', message.messageId); - }, - onWillDismissInAppMessage: (message) => { - this.OSLog('OneSignal: will dismiss IAM: ', message.messageId); - }, - onDidDismissInAppMessage: (message) => { - this.OSLog('OneSignal: did dismiss IAM: ', message.messageId); - }, + OneSignal.InAppMessages.addEventListener('willDisplay', (event) =>{ + this.OSLog('OneSignal: will display IAM: ', event); + }); + + OneSignal.InAppMessages.addEventListener('didDisplay', (event) =>{ + this.OSLog('OneSignal: did display IAM: ', event); + }); + + OneSignal.InAppMessages.addEventListener('willDismiss', (event) =>{ + this.OSLog('OneSignal: will dismiss IAM: ', event); + }); + + OneSignal.InAppMessages.addEventListener('didDismiss', (event) =>{ + this.OSLog('OneSignal: did dismiss IAM: ', event); }); - OneSignal.User.PushSubscription.addChangeHandler((subscription) => { + OneSignal.User.PushSubscription.addObserver((subscription) => { this.OSLog('OneSignal: subscription changed:', subscription); }); - OneSignal.Notifications.addPermissionChangedHandler((granted) => { + OneSignal.Notifications.addPermissionObserver((granted) => { this.OSLog('OneSignal: permission changed:', granted.permission); }); } diff --git a/ios/RCTOneSignal/RCTOneSignal.h b/ios/RCTOneSignal/RCTOneSignal.h index 39ee77b3..99ae3e94 100644 --- a/ios/RCTOneSignal/RCTOneSignal.h +++ b/ios/RCTOneSignal/RCTOneSignal.h @@ -5,7 +5,7 @@ #import "../OneSignalFramework.h" #endif -@interface RCTOneSignal : NSObject +@interface RCTOneSignal : NSObject + (RCTOneSignal *) sharedInstance; diff --git a/ios/RCTOneSignal/RCTOneSignal.m b/ios/RCTOneSignal/RCTOneSignal.m index 9d0308a5..dcaa9234 100644 --- a/ios/RCTOneSignal/RCTOneSignal.m +++ b/ios/RCTOneSignal/RCTOneSignal.m @@ -30,7 +30,7 @@ @implementation RCTOneSignal { BOOL didInitialize; } -OSNotificationOpenedResult* coldStartOSNotificationOpenedResult; +OSNotificationClickResult* coldStartOSNotificationClickResult; + (RCTOneSignal *) sharedInstance { static dispatch_once_t token = 0; @@ -74,28 +74,28 @@ - (void)sendEvent:(NSString *)eventName withBody:(NSDictionary *)body { [RCTOneSignalEventEmitter sendEventWithName:eventName withBody:body]; } -- (void)onOSPushSubscriptionChangedWithStateChanges:(OSPushSubscriptionStateChanges * _Nonnull)stateChanges { - [self sendEvent:OSEventString(SubscriptionChanged) withBody:[stateChanges.to jsonRepresentation]]; +- (void)onPushSubscriptionDidChangeWithState:(OSPushSubscriptionChangedState * _Nonnull)state { + [self sendEvent:OSEventString(SubscriptionChanged) withBody:[state.current jsonRepresentation]]; } -- (void)onOSPermissionChanged:(OSPermissionState *)state { - [self sendEvent:OSEventString(PermissionChanged) withBody:[state jsonRepresentation]]; +- (void)onNotificationPermissionDidChange:(BOOL)permission { + [self sendEvent:OSEventString(PermissionChanged) withBody:@{@"permission": @(permission)}]; } -- (void)onWillDisplayInAppMessage:(OSInAppMessage * _Nonnull)message { - [self sendEvent:OSEventString(InAppMessageWillDisplay) withBody:[message jsonRepresentation]]; +- (void)onWillDisplayInAppMessage:(OSInAppMessageWillDisplayEvent * _Nonnull)event { + [self sendEvent:OSEventString(InAppMessageWillDisplay) withBody:[event.message jsonRepresentation]]; } -- (void)onDidDisplayInAppMessage:(OSInAppMessage * _Nonnull)message { - [self sendEvent:OSEventString(InAppMessageDidDisplay) withBody:[message jsonRepresentation]]; +- (void)onDidDisplayInAppMessage:(OSInAppMessageDidDisplayEvent * _Nonnull)event { + [self sendEvent:OSEventString(InAppMessageDidDisplay) withBody:[event.message jsonRepresentation]]; } -- (void)onWillDismissInAppMessage:(OSInAppMessage * _Nonnull)message { - [self sendEvent:OSEventString(InAppMessageWillDismiss) withBody:[message jsonRepresentation]]; +- (void)onWillDismissInAppMessage:(OSInAppMessageWillDismissEvent * _Nonnull)event { + [self sendEvent:OSEventString(InAppMessageWillDismiss) withBody:[event.message jsonRepresentation]]; } -- (void)onDidDismissInAppMessage:(OSInAppMessage * _Nonnull)message { - [self sendEvent:OSEventString(InAppMessageDidDismiss) withBody:[message jsonRepresentation]]; +- (void)onDidDismissInAppMessage:(OSInAppMessageDidDismissEvent * _Nonnull)event { + [self sendEvent:OSEventString(InAppMessageDidDismiss) withBody:[event.message jsonRepresentation]]; } - (void)dealloc { diff --git a/ios/RCTOneSignal/RCTOneSignalEventEmitter.h b/ios/RCTOneSignal/RCTOneSignalEventEmitter.h index a0a22ced..458446e8 100644 --- a/ios/RCTOneSignal/RCTOneSignalEventEmitter.h +++ b/ios/RCTOneSignal/RCTOneSignalEventEmitter.h @@ -15,7 +15,7 @@ typedef NS_ENUM(NSInteger, OSNotificationEventTypes) { PermissionChanged, SubscriptionChanged, - NotificationWillShowInForeground, + NotificationWillDisplayInForeground, NotificationClicked, InAppMessageClicked, InAppMessageWillDisplay, @@ -24,7 +24,7 @@ typedef NS_ENUM(NSInteger, OSNotificationEventTypes) { InAppMessageDidDismiss, }; -#define OSNotificationEventTypesArray @[@"OneSignal-permissionChanged",@"OneSignal-subscriptionChanged",@"OneSignal-notificationWillShowInForeground",@"OneSignal-notificationClicked",@"OneSignal-inAppMessageClicked", @"OneSignal-inAppMessageWillDisplay", @"OneSignal-inAppMessageDidDisplay", @"OneSignal-inAppMessageWillDismiss", @"OneSignal-inAppMessageDidDismiss"] +#define OSNotificationEventTypesArray @[@"OneSignal-permissionChanged",@"OneSignal-subscriptionChanged",@"OneSignal-notificationWillDisplayInForeground",@"OneSignal-notificationClicked",@"OneSignal-inAppMessageClicked", @"OneSignal-inAppMessageWillDisplay", @"OneSignal-inAppMessageDidDisplay", @"OneSignal-inAppMessageWillDismiss", @"OneSignal-inAppMessageDidDismiss"] #define OSEventString(enum) [OSNotificationEventTypesArray objectAtIndex:enum] diff --git a/ios/RCTOneSignal/RCTOneSignalEventEmitter.m b/ios/RCTOneSignal/RCTOneSignalEventEmitter.m index 3deccd44..be9b2ae2 100644 --- a/ios/RCTOneSignal/RCTOneSignalEventEmitter.m +++ b/ios/RCTOneSignal/RCTOneSignalEventEmitter.m @@ -11,9 +11,9 @@ @implementation RCTOneSignalEventEmitter { BOOL _hasSetPermissionObserver; BOOL _hasSetEmailSubscriptionObserver; BOOL _hasSetSMSSubscriptionObserver; - BOOL _hasSetInAppMessageLifecycleHandler; - NSMutableDictionary* _notificationCompletionCache; - NSMutableDictionary* _receivedNotificationCache; + BOOL _hasAddedInAppMessageLifecycleListener; + NSMutableDictionary* _preventDefaultCache; + NSMutableDictionary* _notificationWillDisplayCache; } static BOOL _didStartObserving = false; @@ -38,8 +38,8 @@ +(BOOL)requiresMainQueueSetup { - (instancetype)init { if (self = [super init]) { - _notificationCompletionCache = [NSMutableDictionary new]; - _receivedNotificationCache = [NSMutableDictionary new]; + _preventDefaultCache = [NSMutableDictionary new]; + _notificationWillDisplayCache = [NSMutableDictionary new]; for (NSString *eventName in [self supportedEvents]) [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(emitEvent:) name:eventName object:nil]; @@ -116,7 +116,7 @@ + (void)sendEventWithName:(NSString *)name withBody:(NSDictionary *)body { RCT_EXPORT_METHOD(enterLiveActivity:(NSString *)activityId withToken:(NSString *)token withResponse:(RCTResponseSenderBlock)callback) { - [OneSignal enterLiveActivity:activityId withToken:token withSuccess:^(NSDictionary *result) { + [OneSignal.LiveActivities enter:activityId withToken:token withSuccess:^(NSDictionary *result) { callback(@[result]); } withFailure:^(NSError *error) { callback([self processNSError:error]); @@ -125,33 +125,21 @@ + (void)sendEventWithName:(NSString *)name withBody:(NSDictionary *)body { RCT_EXPORT_METHOD(exitLiveActivity:(NSString *)activityId withResponse:(RCTResponseSenderBlock)callback) { - [OneSignal exitLiveActivity:activityId withSuccess:^(NSDictionary *result) { + [OneSignal.LiveActivities exit:activityId withSuccess:^(NSDictionary *result) { callback(@[result]); } withFailure:^(NSError *error) { callback([self processNSError:error]); }]; } -RCT_REMAP_METHOD(getPrivacyConsent, - getPrivacyConsentResolver:(RCTPromiseResolveBlock)resolve - rejecter:(RCTPromiseRejectBlock)reject) { - resolve(@(OneSignal.getPrivacyConsent)); -} - -RCT_EXPORT_METHOD(setPrivacyConsent:(BOOL)granted) { +RCT_EXPORT_METHOD(setPrivacyConsentGiven:(BOOL)granted) { dispatch_async(dispatch_get_main_queue(), ^{ - [OneSignal setPrivacyConsent:granted]; + [OneSignal setConsentGiven:granted]; }); } -RCT_REMAP_METHOD(getRequiresPrivacyConsent, - getRequiresPrivacyConsentResolver:(RCTPromiseResolveBlock)resolve - rejecter:(RCTPromiseRejectBlock)reject) { - resolve(@([OneSignal requiresPrivacyConsent])); -} - -RCT_EXPORT_METHOD(setRequiresPrivacyConsent:(BOOL)required) { - [OneSignal setRequiresPrivacyConsent:required]; +RCT_EXPORT_METHOD(setPrivacyConsentRequired:(BOOL)required) { + [OneSignal setConsentRequired:required]; } // OneSignal.Debug namespace methods @@ -194,16 +182,18 @@ + (void)sendEventWithName:(NSString *)name withBody:(NSDictionary *)body { [OneSignal.InAppMessages clearTriggers]; } -RCT_EXPORT_METHOD(setInAppMessageClickHandler) { - [OneSignal.InAppMessages setClickHandler:^(OSInAppMessageAction *action) { - [RCTOneSignalEventEmitter sendEventWithName:@"OneSignal-inAppMessageClicked" withBody:[action jsonRepresentation]]; - }]; +RCT_EXPORT_METHOD(addInAppMessageClickListener) { + [OneSignal.InAppMessages addClickListener:self]; } -RCT_EXPORT_METHOD(setInAppMessagesLifecycleHandler) { - if (!_hasSetInAppMessageLifecycleHandler) { - [OneSignal.InAppMessages setLifecycleHandler:[RCTOneSignal sharedInstance]]; - _hasSetInAppMessageLifecycleHandler = true; +RCT_EXPORT_METHOD(onClickInAppMessage:(OSInAppMessageClickEvent * _Nonnull)event { + [RCTOneSignalEventEmitter sendEventWithName:@"OneSignal-inAppMessageClicked" withBody:[event jsonRepresentation]]; +}) + +RCT_EXPORT_METHOD(addInAppMessagesLifecycleListener) { + if (!_hasAddedInAppMessageLifecycleListener) { + [OneSignal.InAppMessages addLifecycleListener:[RCTOneSignal sharedInstance]]; + _hasAddedInAppMessageLifecycleListener = true; } } @@ -250,40 +240,62 @@ + (void)sendEventWithName:(NSString *)name withBody:(NSDictionary *)body { }]; } -RCT_EXPORT_METHOD(addPermissionChangedHandler) { +RCT_EXPORT_METHOD(addPermissionObserver) { if (!_hasSetPermissionObserver) { [OneSignal.Notifications addPermissionObserver:[RCTOneSignal sharedInstance]]; _hasSetPermissionObserver = true; } } -RCT_EXPORT_METHOD(removePermissionChangedHandler) { +RCT_EXPORT_METHOD(removePermissionObserver) { if (_hasSetPermissionObserver) { [OneSignal.Notifications removePermissionObserver:[RCTOneSignal sharedInstance]]; _hasSetPermissionObserver = false; } } -RCT_EXPORT_METHOD(setNotificationClickHandler) { - [OneSignal.Notifications setNotificationOpenedHandler:^(OSNotificationOpenedResult *result) { - [RCTOneSignalEventEmitter sendEventWithName:@"OneSignal-notificationClicked" withBody:[result jsonRepresentation]]; - }]; +RCT_REMAP_METHOD(permissionNative, + getPermissionNativeResolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) { + resolve(@([OneSignal.Notifications permissionNative])); +} + +RCT_EXPORT_METHOD(addNotificationClickListener) { + [OneSignal.Notifications addClickListener:self]; +} + +RCT_EXPORT_METHOD(onClickNotification:(OSNotificationClickEvent * _Nonnull)event) { + [RCTOneSignalEventEmitter sendEventWithName:@"OneSignal-notificationClicked" withBody:[event jsonRepresentation]]; } RCT_EXPORT_METHOD(clearAllNotifications) { [OneSignal.Notifications clearAll]; } -RCT_EXPORT_METHOD(setNotificationWillShowInForegroundHandler) { +RCT_EXPORT_METHOD(addNotificationForegroundLifecycleListener) { + [OneSignal.Notifications addForegroundLifecycleListener:self]; +} + +RCT_EXPORT_METHOD(onWillDisplayNotification:(OSNotificationWillDisplayEvent *)event){ __weak RCTOneSignalEventEmitter *weakSelf = self; - [OneSignal.Notifications setNotificationWillShowInForegroundHandler:^(OSNotification *notif, OSNotificationDisplayResponse completion) { - RCTOneSignalEventEmitter *strongSelf = weakSelf; - if (!strongSelf) return; + RCTOneSignalEventEmitter *strongSelf = weakSelf; + if (!strongSelf) return; - strongSelf->_receivedNotificationCache[notif.notificationId] = notif; - strongSelf->_notificationCompletionCache[notif.notificationId] = completion; - [RCTOneSignalEventEmitter sendEventWithName:@"OneSignal-notificationWillShowInForeground" withBody:[notif jsonRepresentation]]; - }]; + strongSelf->_notificationWillDisplayCache[event.notification.notificationId] = event; + [event preventDefault]; + [RCTOneSignalEventEmitter sendEventWithName:@"OneSignal-notificationWillDisplayInForeground" withBody:[event.notification jsonRepresentation]]; +} + +RCT_EXPORT_METHOD(preventDefault:(NSString *)notificationId){ + __weak RCTOneSignalEventEmitter *weakSelf = self; + RCTOneSignalEventEmitter *strongSelf = weakSelf; + OSNotificationWillDisplayEvent *event = _notificationWillDisplayCache[notificationId]; + if (!event) { + [OneSignalLog onesignalLog:ONE_S_LL_ERROR message:[NSString stringWithFormat:@"OneSignal (objc): could not find notification will display event for notification with id: %@", notificationId]]; + return; + } + strongSelf->_preventDefaultCache[event.notification.notificationId] = event; + [event preventDefault]; } // OneSignal.Session namespace methods @@ -300,14 +312,14 @@ + (void)sendEventWithName:(NSString *)name withBody:(NSDictionary *)body { } // OneSignal.User namespace methods -RCT_EXPORT_METHOD(addPushSubscriptionChangeHandler) { +RCT_EXPORT_METHOD(addPushSubscriptionObserver) { if (!_hasSetSubscriptionObserver) { [OneSignal.User.pushSubscription addObserver:[RCTOneSignal sharedInstance]]; _hasSetSubscriptionObserver = true; } } -RCT_EXPORT_METHOD(removePushSubscriptionChangeHandler) { +RCT_EXPORT_METHOD(removePushSubscriptionObserver) { if (_hasSetSubscriptionObserver) { [OneSignal.User.pushSubscription removeObserver:[RCTOneSignal sharedInstance]]; _hasSetSubscriptionObserver = false; @@ -408,21 +420,18 @@ + (void)sendEventWithName:(NSString *)name withBody:(NSDictionary *)body { [OneSignal.User.pushSubscription optOut]; } -RCT_EXPORT_METHOD(completeNotificationEvent:(NSString*)notificationId displayOption:(BOOL)shouldDisplay) { - OSNotificationDisplayResponse completion = _notificationCompletionCache[notificationId]; - if (!completion) return; - - if (shouldDisplay) { - OSNotification *notif = _receivedNotificationCache[notificationId]; - dispatch_async(dispatch_get_main_queue(), ^{ - completion(notif); - }); - } else { - completion(nil); +RCT_EXPORT_METHOD(displayNotification:(NSString*)notificationId) { + OSNotificationWillDisplayEvent *event = _notificationWillDisplayCache[notificationId]; + if (!event) { + [OneSignalLog onesignalLog:ONE_S_LL_ERROR message:[NSString stringWithFormat:@"OneSignal (objc): could not find notification will display event for notification with id: %@", notificationId]]; + return; } + dispatch_async(dispatch_get_main_queue(), ^{ + [event.notification display]; + }); - [_notificationCompletionCache removeObjectForKey:notificationId]; - [_receivedNotificationCache removeObjectForKey:notificationId]; + [_preventDefaultCache removeObjectForKey:notificationId]; + [_notificationWillDisplayCache removeObjectForKey:notificationId]; } RCT_EXPORT_METHOD(initInAppMessageClickHandlerParams) { diff --git a/react-native-onesignal.podspec b/react-native-onesignal.podspec index 9c74497c..248b431b 100644 --- a/react-native-onesignal.podspec +++ b/react-native-onesignal.podspec @@ -22,5 +22,5 @@ Pod::Spec.new do |s| # pod 'React', :path => '../node_modules/react-native/' # The Native OneSignal-iOS-SDK XCFramework from cocoapods. - s.dependency 'OneSignalXCFramework', '5.0.0-beta-01' + s.dependency 'OneSignalXCFramework', '5.0.0-beta-04' end diff --git a/src/OSNotification.ts b/src/OSNotification.ts index 63e46e17..ffc42747 100644 --- a/src/OSNotification.ts +++ b/src/OSNotification.ts @@ -1,4 +1,5 @@ -import { Platform } from 'react-native'; +import { NativeModules, Platform } from 'react-native'; +const RNOneSignal = NativeModules.OneSignal; export default class OSNotification { body: string; @@ -76,4 +77,9 @@ export default class OSNotification { this.interruptionLevel = receivedEvent.interruptionLevel; } } + + display(): void { + RNOneSignal.displayNotification(this.notificationId); + return; + } } diff --git a/src/events/EventManager.ts b/src/events/EventManager.ts index 8768b719..400fda5b 100644 --- a/src/events/EventManager.ts +++ b/src/events/EventManager.ts @@ -3,12 +3,12 @@ import { NativeEventEmitter, NativeModule, } from 'react-native'; -import NotificationReceivedEvent from './NotificationReceivedEvent'; +import NotificationWillDisplayEvent from './NotificationWillDisplayEvent'; import { isMultipleInstancesPossible } from '../helpers'; import { PERMISSION_CHANGED, SUBSCRIPTION_CHANGED, - NOTIFICATION_WILL_SHOW, + NOTIFICATION_WILL_DISPLAY, NOTIFICATION_CLICKED, IN_APP_MESSAGE_CLICKED, IN_APP_MESSAGE_WILL_DISPLAY, @@ -21,7 +21,7 @@ import OSNotification from '../OSNotification'; const eventList = [ PERMISSION_CHANGED, SUBSCRIPTION_CHANGED, - NOTIFICATION_WILL_SHOW, + NOTIFICATION_WILL_DISPLAY, NOTIFICATION_CLICKED, IN_APP_MESSAGE_CLICKED, IN_APP_MESSAGE_WILL_DISPLAY, @@ -126,8 +126,8 @@ export default class EventManager { getFinalPayload(eventName: string, payload: Object): Object { switch (eventName) { - case NOTIFICATION_WILL_SHOW: - return new NotificationReceivedEvent(payload as OSNotification); + case NOTIFICATION_WILL_DISPLAY: + return new NotificationWillDisplayEvent(payload as OSNotification); default: return payload; } diff --git a/src/events/NotificationReceivedEvent.ts b/src/events/NotificationReceivedEvent.ts deleted file mode 100644 index a8c3f7f2..00000000 --- a/src/events/NotificationReceivedEvent.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { NativeModules } from 'react-native'; -import OSNotification from '../OSNotification'; -const RNOneSignal = NativeModules.OneSignal; - -export default class NotificationReceivedEvent { - private notification: OSNotification; - - constructor(receivedEvent: OSNotification) { - this.notification = new OSNotification(receivedEvent); - } - - complete(notification?: OSNotification) { - if (!notification) { - // if the notificationReceivedEvent is null, we want to call the native-side - // complete/completion with null to silence the notification - RNOneSignal.completeNotificationEvent( - this.notification.notificationId, - false, - ); - return; - } - - // if the notificationReceivedEvent is not null, we want to pass the specific event - // future: Android side: make the notification modifiable - // iOS & Android: the notification id is associated with the native-side complete handler / completion block - RNOneSignal.completeNotificationEvent(notification.notificationId, true); - } - - getNotification(): OSNotification { - return this.notification; - } -} diff --git a/src/events/NotificationWillDisplayEvent.ts b/src/events/NotificationWillDisplayEvent.ts new file mode 100644 index 00000000..e647aba9 --- /dev/null +++ b/src/events/NotificationWillDisplayEvent.ts @@ -0,0 +1,20 @@ +import { NativeModules } from 'react-native'; +import OSNotification from '../OSNotification'; +const RNOneSignal = NativeModules.OneSignal; + +export default class NotificationWillDisplayEvent { + public notification: OSNotification; + + constructor(displayEvent: OSNotification) { + this.notification = new OSNotification(displayEvent); + } + + preventDefault(): void { + RNOneSignal.preventDefault(this.notification.notificationId); + return; + } + + getNotification(): OSNotification { + return this.notification; + } +} diff --git a/src/events/events.ts b/src/events/events.ts index c59fb052..3ac0c57d 100644 --- a/src/events/events.ts +++ b/src/events/events.ts @@ -1,5 +1,5 @@ -export const NOTIFICATION_WILL_SHOW = - 'OneSignal-notificationWillShowInForeground'; +export const NOTIFICATION_WILL_DISPLAY = + 'OneSignal-notificationWillDisplayInForeground'; export const NOTIFICATION_CLICKED = 'OneSignal-notificationClicked'; export const IN_APP_MESSAGE_CLICKED = 'OneSignal-inAppMessageClicked'; export const IN_APP_MESSAGE_WILL_DISPLAY = 'OneSignal-inAppMessageWillDisplay'; diff --git a/src/index.ts b/src/index.ts index 5890089d..f74995ef 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,18 +9,26 @@ import { IN_APP_MESSAGE_WILL_DISMISS, IN_APP_MESSAGE_WILL_DISPLAY, NOTIFICATION_CLICKED, - NOTIFICATION_WILL_SHOW, + NOTIFICATION_WILL_DISPLAY, PERMISSION_CHANGED, SUBSCRIPTION_CHANGED, } from './events/events'; -import { PushSubscription } from './models/Subscription'; -import NotificationReceivedEvent from './events/NotificationReceivedEvent'; -import { OpenedEvent } from './models/NotificationEvents'; +import { NotificationEventName, + NotificationEventTypeMap, + NotificationClickedEvent} from './models/NotificationEvents'; +import { PushSubscription, + OSNotificationPermission } from './models/Subscription'; +import NotificationWillDisplayEvent from './events/NotificationWillDisplayEvent'; import { OutcomeEvent } from './models/Outcomes'; import { InAppMessage, - InAppMessageAction, - InAppMessageLifecycleHandlerObject, + InAppMessageEventTypeMap, + InAppMessageEventName, + InAppMessageClickEvent, + InAppMessageWillDisplayEvent, + InAppMessageDidDisplayEvent, + InAppMessageWillDismissEvent, + InAppMessageDidDismissEvent, } from './models/InAppMessage'; import { isValidCallback, isNativeModuleLoaded } from './helpers'; @@ -40,16 +48,16 @@ let pushSubscription: PushSubscription = { optedIn: false, }; -async function setNotificationPermissionChangeHandler() { - OneSignal.Notifications.addPermissionChangedHandler((granted) => { +async function addPermissionObserver() { + OneSignal.Notifications.addPermissionObserver((granted) => { notificationPermission = granted.permission; }); notificationPermission = await RNOneSignal.hasNotificationPermission(); } -async function setPushSubscriptionChangeHandler() { - OneSignal.User.PushSubscription.addChangeHandler((subscriptionChange) => { +async function addPushSubscriptionObserver() { + OneSignal.User.PushSubscription.addObserver((subscriptionChange) => { pushSubscription = subscriptionChange; }); @@ -65,8 +73,8 @@ export namespace OneSignal { RNOneSignal.initialize(appId); - setNotificationPermissionChangeHandler(); - setPushSubscriptionChangeHandler(); + addPermissionObserver(); + addPushSubscriptionObserver(); } /** @@ -89,29 +97,11 @@ export namespace OneSignal { RNOneSignal.logout(); } - /** True if the application requires user privacy consent, false otherwise. */ - export function getRequiresPrivacyConsent(): Promise { - if (!isNativeModuleLoaded(RNOneSignal)) { - return Promise.reject(new Error('OneSignal native module not loaded')); - } - - return RNOneSignal.getRequiresPrivacyConsent(); - } - /** For GDPR users, your application should call this method before setting the App ID. */ - export function setRequiresPrivacyConsent(required: boolean) { + export function setConsentRequired(required: boolean) { if (!isNativeModuleLoaded(RNOneSignal)) return; - RNOneSignal.setRequiresPrivacyConsent(required); - } - - /** Indicates whether privacy consent has been granted. */ - export function getPrivacyConsent(): Promise { - if (!isNativeModuleLoaded(RNOneSignal)) { - return Promise.reject(new Error('OneSignal native module not loaded')); - } - - return RNOneSignal.getPrivacyConsent(); + RNOneSignal.setPrivacyConsentRequired(required); } /** @@ -119,10 +109,10 @@ export namespace OneSignal { * Indicates whether privacy consent has been granted. This field is only relevant when the application has opted * into data privacy protections. */ - export function setPrivacyConsent(granted: boolean) { + export function setConsentGiven(granted: boolean) { if (!isNativeModuleLoaded(RNOneSignal)) return; - RNOneSignal.setPrivacyConsent(granted); + RNOneSignal.setPrivacyConsentGiven(granted); } /** This method can be used to set if launch URLs should be opened in safari or within the application. */ @@ -138,40 +128,6 @@ export namespace OneSignal { } } - /** Associates a temporary push token with an Activity ID on the OneSignal server. */ - export function enterLiveActivity( - activityId: string, - token: string, - handler?: Function, - ) { - if (!isNativeModuleLoaded(RNOneSignal)) return; - - if (!handler) { - handler = () => {}; - } - - // Only Available on iOS - if (Platform.OS === 'ios') { - RNOneSignal.enterLiveActivity(activityId, token, handler); - } - } - - /** - * Deletes activityId associated temporary push token on the OneSignal server. - */ - export function exitLiveActivity(activityId: string, handler?: Function) { - if (!isNativeModuleLoaded(RNOneSignal)) return; - - if (!handler) { - handler = () => {}; - } - - // Only Available on iOS - if (Platform.OS === 'ios') { - RNOneSignal.exitLiveActivity(activityId, handler); - } - } - /** Clears all handlers and observers. */ export function clearHandlers() { if (!isNativeModuleLoaded(RNOneSignal)) return; @@ -201,16 +157,54 @@ export namespace OneSignal { } } + export namespace LiveActivities { + /** + * Associates a temporary push token with an Activity ID on the OneSignal server. + */ + export function enter( + activityId: string, + token: string, + handler?: Function, + ) { + if (!isNativeModuleLoaded(RNOneSignal)) return; + + if (!handler) { + handler = () => {}; + } + + // Only Available on iOS + if (Platform.OS === 'ios') { + RNOneSignal.enterLiveActivity(activityId, token, handler); + } + } + + /** + * Deletes activityId associated temporary push token on the OneSignal server. + */ + export function exit(activityId: string, handler?: Function) { + if (!isNativeModuleLoaded(RNOneSignal)) return; + + if (!handler) { + handler = () => {}; + } + + // Only Available on iOS + if (Platform.OS === 'ios') { + RNOneSignal.exitLiveActivity(activityId, handler); + } + } + } + export namespace User { export namespace PushSubscription { /** Add a callback that fires when the OneSignal subscription state changes. */ - export function addChangeHandler( + export function addObserver( handler: (event: PushSubscription) => void, ) { if (!isNativeModuleLoaded(RNOneSignal)) return; isValidCallback(handler); - RNOneSignal.addPushSubscriptionChangeHandler(); + RNOneSignal.addPushSubscriptionObserver(); eventManager.addEventHandler( SUBSCRIPTION_CHANGED, handler, @@ -218,10 +212,10 @@ export namespace OneSignal { } /** Clears current subscription observers. */ - export function removeChangeHandler() { + export function removeObserver() { if (!isNativeModuleLoaded(RNOneSignal)) return; - RNOneSignal.removePushSubscriptionChangeHandler(); + RNOneSignal.removePushSubscriptionObserver(); eventManager.clearEventHandler(SUBSCRIPTION_CHANGED); } @@ -402,6 +396,9 @@ export namespace OneSignal { } export namespace Notifications { + export const _notificationClickedListeners: ((action: NotificationClickedEvent) => void)[] = []; + export const _notificationWillDisplayListeners: ((notification: NotificationWillDisplayEvent) => void)[] = []; + /** * Whether this app has push notification permission. Returns true if the user has accepted permissions, * or if the app has ephemeral or provisional permission. @@ -458,13 +455,13 @@ export namespace OneSignal { } /** Add a callback that fires when the native push permission changes. */ - export function addPermissionChangedHandler( + export function addPermissionObserver( observer: (event: { permission: boolean }) => void, ) { if (!isNativeModuleLoaded(RNOneSignal)) return; isValidCallback(observer); - RNOneSignal.addPermissionChangedHandler(); + RNOneSignal.addPermissionObserver(); eventManager.addEventHandler<{ permission: boolean }>( PERMISSION_CHANGED, observer, @@ -472,42 +469,70 @@ export namespace OneSignal { } /** Remove permission observer that have been previously added. */ - export function removePermissionChangedHandler() { + export function removePermissionObserver() { if (!isNativeModuleLoaded(RNOneSignal)) return; - RNOneSignal.removePermissionChangedHandler(); + RNOneSignal.removePermissionObserver(); eventManager.clearEventHandler(PERMISSION_CHANGED); } - /** Sets a handler that will run whenever a notification is opened by the user. */ - export function setNotificationClickHandler( - handler: (openedEvent: OpenedEvent) => void, - ) { + /** iOS Only. + * Returns the enum for the native permission of the device. It will be one of: + * OSNotificationPermissionNotDetermined, + * OSNotificationPermissionDenied, + * OSNotificationPermissionAuthorized, + * OSNotificationPermissionProvisional - only available in iOS 12, + * OSNotificationPermissionEphemeral - only available in iOS 14 + * */ + export function permissionNative() { if (!isNativeModuleLoaded(RNOneSignal)) return; - isValidCallback(handler); - - RNOneSignal.setNotificationClickHandler(); - eventManager.setEventHandler(NOTIFICATION_CLICKED, handler); + if (Platform.OS === 'ios') { + return RNOneSignal.permissionNative(); + } else { + return notificationPermission ? OSNotificationPermission.Authorized : OSNotificationPermission.Denied; + } } /** - * Sets the handler to run before displaying a notification while the app is in focus. Use this handler to read notification - * data and change it or decide if the notification should show or not. - * Note: this runs after the Notification Service Extension which can be used to modify the notification before showing it. - */ - export function setNotificationWillShowInForegroundHandler( - handler: (event: NotificationReceivedEvent) => void, - ) { + * Add listeners for notification click and/or lifecycle events. */ + export function addEventListener(event: K, listener: (event: NotificationEventTypeMap[K]) => void): void { if (!isNativeModuleLoaded(RNOneSignal)) return; + isValidCallback(listener); - isValidCallback(handler); + if (event === "click") { + _notificationClickedListeners.push(listener as (event: NotificationClickedEvent) => void); + RNOneSignal.addNotificationClickListener(); + eventManager.setEventHandler( + NOTIFICATION_CLICKED, + listener as (event: NotificationClickedEvent) => void + ); + } else if (event === "foregroundWillDisplay") { + _notificationWillDisplayListeners.push(listener as (event: NotificationWillDisplayEvent) => void); + RNOneSignal.addNotificationForegroundLifecycleListener(); + eventManager.setEventHandler( + NOTIFICATION_WILL_DISPLAY, + listener as (event: NotificationWillDisplayEvent) => void + ); + } + } - RNOneSignal.setNotificationWillShowInForegroundHandler(); - eventManager.setEventHandler( - NOTIFICATION_WILL_SHOW, - handler, - ); + /** + * Remove listeners for notification click and/or lifecycle events. */ + export function removeEventListener(event: K, listener: (obj: NotificationEventTypeMap[K]) => void): void { + if (event === "click") { + let index = _notificationClickedListeners.indexOf(listener as (event: NotificationClickedEvent) => void); + if (index !== -1) { + _notificationClickedListeners.splice(index, 1); + } + } else if (event === "foregroundWillDisplay") { + let index = _notificationWillDisplayListeners.indexOf(listener as (event: NotificationWillDisplayEvent) => void); + if (index !== -1) { + _notificationWillDisplayListeners.splice(index, 1); + } + } else { + return; + } } /** @@ -555,63 +580,110 @@ export namespace OneSignal { } export namespace InAppMessages { - /** Set the in-app message click handler. */ - export function setClickHandler( - handler: (action: InAppMessageAction) => void, - ) { - if (!isNativeModuleLoaded(RNOneSignal)) return; - - isValidCallback(handler); - RNOneSignal.setInAppMessageClickHandler(); - eventManager.setEventHandler( - IN_APP_MESSAGE_CLICKED, - handler, - ); - } + export const _inAppMessageClickListeners: ((action: InAppMessageClickEvent) => void)[] = []; + export const _willDisplayInAppMessageListeners: ((event: InAppMessageWillDisplayEvent) => void) [] = []; + export const _didDisplayInAppMessageListeners: ((event: InAppMessageDidDisplayEvent) => void) [] = []; + export const _willDismissInAppMessageListeners: ((event: InAppMessageWillDismissEvent) => void) [] = []; + export const _didDismissInAppMessageListeners: ((event: InAppMessageDidDismissEvent) => void) [] = []; - /** Set the in-app message lifecycle handler. */ - export function setLifecycleHandler( - handlerObject: InAppMessageLifecycleHandlerObject, - ) { - if (!isNativeModuleLoaded(RNOneSignal)) return; - - if (handlerObject.onWillDisplayInAppMessage) { - isValidCallback(handlerObject.onWillDisplayInAppMessage); - eventManager.setEventHandler( - IN_APP_MESSAGE_WILL_DISPLAY, - handlerObject.onWillDisplayInAppMessage, - ); - } - if (handlerObject.onDidDisplayInAppMessage) { - isValidCallback(handlerObject.onDidDisplayInAppMessage); - eventManager.setEventHandler( - IN_APP_MESSAGE_DID_DISPLAY, - handlerObject.onDidDisplayInAppMessage, - ); + /** + * Add listeners for notification click and/or lifecycle events. + */ + export function addEventListener(event: K, listener: (event: InAppMessageEventTypeMap[K]) => void): void { + if (!isNativeModuleLoaded(RNOneSignal)) { + return; } - if (handlerObject.onWillDismissInAppMessage) { - isValidCallback(handlerObject.onWillDismissInAppMessage); - eventManager.setEventHandler( - IN_APP_MESSAGE_WILL_DISMISS, - handlerObject.onWillDismissInAppMessage, + + if (event === "click") { + isValidCallback(listener); + _inAppMessageClickListeners.push(listener as (event: InAppMessageClickEvent) => void); + RNOneSignal.addInAppMessageClickListener(); + eventManager.setEventHandler( + IN_APP_MESSAGE_CLICKED, + listener as (event: InAppMessageClickEvent) => void ); } - if (handlerObject.onDidDismissInAppMessage) { - isValidCallback(handlerObject.onDidDismissInAppMessage); - eventManager.setEventHandler( - IN_APP_MESSAGE_DID_DISMISS, - handlerObject.onDidDismissInAppMessage, - ); + else{ + if (event === "willDisplay") { + isValidCallback(listener); + _willDisplayInAppMessageListeners.push(listener as (event: InAppMessageWillDisplayEvent) => void); + eventManager.setEventHandler( + IN_APP_MESSAGE_WILL_DISPLAY, + listener as (event: InAppMessageWillDisplayEvent) => void + ); + } + else if (event === "didDisplay") { + isValidCallback(listener); + _didDisplayInAppMessageListeners.push(listener as (event: InAppMessageDidDisplayEvent) => void); + eventManager.setEventHandler( + IN_APP_MESSAGE_DID_DISPLAY, + listener as (event: InAppMessageDidDisplayEvent) => void + ); + } + else if (event === "willDismiss"){ + isValidCallback(listener); + _willDismissInAppMessageListeners.push(listener as (event: InAppMessageWillDismissEvent) => void); + eventManager.setEventHandler( + IN_APP_MESSAGE_WILL_DISMISS, + listener as (event: InAppMessageWillDismissEvent) => void + ); + } + else if (event === "didDismiss"){ + isValidCallback(listener); + _didDismissInAppMessageListeners.push(listener as (event: InAppMessageDidDismissEvent) => void); + eventManager.setEventHandler( + IN_APP_MESSAGE_DID_DISMISS, + listener as (event: InAppMessageDidDismissEvent) => void + ); + } + else { + return; + } + RNOneSignal.addInAppMessagesLifecycleListener(); } + } - RNOneSignal.setInAppMessagesLifecycleHandler(); + /** + * Remove listeners for In-App Message click and/or lifecycle events. + */ + export function removeEventListener(event: K, listener: (obj: InAppMessageEventTypeMap[K]) => void): void { + if (event === "click") { + const index = _inAppMessageClickListeners.indexOf(listener); + if (index !== -1) { + _inAppMessageClickListeners.splice(index, 1); + } + } else { + if (event === "willDisplay") { + let index = _willDisplayInAppMessageListeners.indexOf(listener as (event: InAppMessageWillDisplayEvent) => void); + if (index !== -1) { + _willDisplayInAppMessageListeners.splice(index, 1); + } + } else if (event === "didDisplay") { + let index = _didDisplayInAppMessageListeners.indexOf(listener as (event: InAppMessageDidDisplayEvent) => void); + if (index !== -1) { + _willDisplayInAppMessageListeners.splice(index, 1); + } + } else if (event === "willDismiss") { + let index = _willDismissInAppMessageListeners.indexOf(listener as (event: InAppMessageWillDismissEvent) => void); + if (index !== -1) { + _willDismissInAppMessageListeners.splice(index, 1); + } + } else if (event === "didDismiss") { + let index = _didDismissInAppMessageListeners.indexOf(listener as (event: InAppMessageDidDismissEvent) => void); + if (index !== -1) { + _didDismissInAppMessageListeners.splice(index, 1); + } + } else { + return; + } + } } /** * Add a trigger for the current user. Triggers are currently explicitly used to determine whether a specific IAM should be * displayed to the user. */ - export function addTrigger(key: string, value: string | number | boolean) { + export function addTrigger(key: string, value: string) { if (!isNativeModuleLoaded(RNOneSignal)) return; // value can be assigned to `false` so we cannot just check `!value` @@ -619,7 +691,7 @@ export namespace OneSignal { console.error('OneSignal: addTrigger: must include a key and a value'); } - let trigger: { [key: string]: string | number | boolean } = {}; + let trigger: { [key: string]: string } = {}; trigger[key] = value; RNOneSignal.addTriggers(trigger); } @@ -629,7 +701,7 @@ export namespace OneSignal { * be displayed to the user. */ export function addTriggers(triggers: { - [key: string]: string | number | boolean; + [key: string]: string; }) { if (!isNativeModuleLoaded(RNOneSignal)) return; @@ -742,17 +814,20 @@ export namespace OneSignal { } export { - NotificationReceivedEvent, - OpenedEvent, + NotificationWillDisplayEvent, + NotificationClickedEvent, InAppMessage, - InAppMessageAction, - InAppMessageLifecycleHandlerObject, + InAppMessageClickEvent, + InAppMessageWillDisplayEvent, + InAppMessageDidDisplayEvent, + InAppMessageWillDismissEvent, + InAppMessageDidDismissEvent, OutcomeEvent, }; export { default as OSNotification } from './OSNotification'; export { - OpenedEventAction, - OpenedEventActionType, + ClickedEventAction, + ClickedEventActionType, } from './models/NotificationEvents'; -export { IosPermissionStatus } from './models/Subscription'; +export { OSNotificationPermission } from './models/Subscription'; diff --git a/src/models/InAppMessage.ts b/src/models/InAppMessage.ts index 30217580..ff640c64 100644 --- a/src/models/InAppMessage.ts +++ b/src/models/InAppMessage.ts @@ -1,19 +1,41 @@ +export type InAppMessageEventName = "click" | "willDisplay" | "didDisplay" | "willDismiss" | "didDismiss"; + +export type InAppMessageEventTypeMap = { + click: InAppMessageClickEvent; + willDisplay: InAppMessageWillDisplayEvent; + didDisplay: InAppMessageDidDisplayEvent; + willDismiss: InAppMessageWillDismissEvent; + didDismiss: InAppMessageDidDismissEvent; +}; + export interface InAppMessage { messageId: string; } -export interface InAppMessageAction { - closes_message: boolean; - first_click: boolean; - click_name?: string; - click_url?: string; - outcomes?: object[]; - tags?: object; +export interface InAppMessageClickEvent { + message : InAppMessage + result : InAppMessageClickResult +} + +export interface InAppMessageClickResult { + closingMessage: boolean; + actionId?: string; + url?: string; + urlTarget?: string; +} + +export interface InAppMessageWillDisplayEvent { + message : InAppMessage +} + +export interface InAppMessageDidDisplayEvent { + message : InAppMessage +} + +export interface InAppMessageWillDismissEvent { + message : InAppMessage } -export interface InAppMessageLifecycleHandlerObject { - onWillDisplayInAppMessage?: (message: InAppMessage) => void; - onDidDisplayInAppMessage?: (message: InAppMessage) => void; - onWillDismissInAppMessage?: (message: InAppMessage) => void; - onDidDismissInAppMessage?: (message: InAppMessage) => void; +export interface InAppMessageDidDismissEvent { + message : InAppMessage } diff --git a/src/models/NotificationEvents.ts b/src/models/NotificationEvents.ts index e1a7145e..d1e72cb3 100644 --- a/src/models/NotificationEvents.ts +++ b/src/models/NotificationEvents.ts @@ -1,13 +1,22 @@ import OSNotification from '../OSNotification'; +import NotificationWillDisplayEvent from '../events/NotificationWillDisplayEvent'; + +export type NotificationEventName = "click" | "foregroundWillDisplay"; + +export type NotificationEventTypeMap = { + click: NotificationClickedEvent; + foregroundWillDisplay: NotificationWillDisplayEvent; +}; // 0 = NotificationClicked, 1 = ButtonClicked -export type OpenedEventActionType = 0 | 1; +export type ClickedEventActionType = 0 | 1; -export interface OpenedEvent { - action: OpenedEventAction; +export interface NotificationClickedEvent { + action: ClickedEventAction; notification: OSNotification; } -export interface OpenedEventAction { - type: OpenedEventActionType; +export interface ClickedEventAction { + actionId ?: string; + type: ClickedEventActionType; } diff --git a/src/models/Subscription.ts b/src/models/Subscription.ts index 54b022f1..5b3aefb7 100644 --- a/src/models/Subscription.ts +++ b/src/models/Subscription.ts @@ -1,5 +1,10 @@ -// 0 = NotDetermined, 1 = Denied, 2 = Authorized, 3 = Provisional, 4 = Ephemeral -export type IosPermissionStatus = 0 | 1 | 2 | 3 | 4; +export enum OSNotificationPermission { + NotDetermined = 0, + Denied, + Authorized, + Provisional, // only available in iOS 12 + Ephemeral // only available in iOS 14 +} export interface PushSubscription { id: string;