diff --git a/android/build.gradle b/android/build.gradle index 54a1160c..6426f0fa 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.1.35' + api 'com.onesignal:OneSignal:5.3.0-alpha-01' testImplementation 'junit:junit:4.12' } \ No newline at end of file diff --git a/android/src/main/java/com/onesignal/rnonesignalandroid/RNOneSignal.java b/android/src/main/java/com/onesignal/rnonesignalandroid/RNOneSignal.java index 773a8a3e..05e6fd3d 100644 --- a/android/src/main/java/com/onesignal/rnonesignalandroid/RNOneSignal.java +++ b/android/src/main/java/com/onesignal/rnonesignalandroid/RNOneSignal.java @@ -35,11 +35,15 @@ of this software and associated documentation files (the "Software"), to deal package com.onesignal.rnonesignalandroid; +import java.util.HashMap; +import java.util.Map; + import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.os.Bundle; -import com.onesignal.debug.internal.logging.Logging; -import com.facebook.react.bridge.Callback; + +import org.jetbrains.annotations.Nullable; +import org.json.JSONException; + +import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.LifecycleEventListener; import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.ReactApplicationContext; @@ -48,39 +52,31 @@ of this software and associated documentation files (the "Software"), to deal import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.modules.core.DeviceEventManagerModule; import com.facebook.react.bridge.WritableMap; -import com.facebook.react.bridge.Arguments; +import com.facebook.react.modules.core.DeviceEventManagerModule; import com.onesignal.Continue; import com.onesignal.OneSignal; -import com.onesignal.debug.LogLevel; import com.onesignal.common.OneSignalWrapper; -import com.onesignal.inAppMessages.IInAppMessage; -import com.onesignal.inAppMessages.IInAppMessageClickListener; +import com.onesignal.debug.LogLevel; +import com.onesignal.debug.internal.logging.Logging; import com.onesignal.inAppMessages.IInAppMessageClickEvent; -import com.onesignal.inAppMessages.IInAppMessageClickResult; -import com.onesignal.inAppMessages.IInAppMessageLifecycleListener; -import com.onesignal.inAppMessages.IInAppMessageWillDisplayEvent; +import com.onesignal.inAppMessages.IInAppMessageClickListener; +import com.onesignal.inAppMessages.IInAppMessageDidDismissEvent; import com.onesignal.inAppMessages.IInAppMessageDidDisplayEvent; +import com.onesignal.inAppMessages.IInAppMessageLifecycleListener; import com.onesignal.inAppMessages.IInAppMessageWillDismissEvent; -import com.onesignal.inAppMessages.IInAppMessageDidDismissEvent; +import com.onesignal.inAppMessages.IInAppMessageWillDisplayEvent; import com.onesignal.notifications.INotification; -import com.onesignal.notifications.INotificationClickListener; import com.onesignal.notifications.INotificationClickEvent; +import com.onesignal.notifications.INotificationClickListener; import com.onesignal.notifications.INotificationLifecycleListener; import com.onesignal.notifications.INotificationWillDisplayEvent; import com.onesignal.notifications.IPermissionObserver; +import com.onesignal.user.state.IUserStateObserver; +import com.onesignal.user.state.UserChangedState; import com.onesignal.user.subscriptions.IPushSubscription; import com.onesignal.user.subscriptions.IPushSubscriptionObserver; -import com.onesignal.user.subscriptions.PushSubscriptionState; import com.onesignal.user.subscriptions.PushSubscriptionChangedState; -import com.onesignal.user.state.UserState; -import com.onesignal.user.state.UserChangedState; -import com.onesignal.user.state.IUserStateObserver; -import org.json.JSONException; - -import java.util.HashMap; -import java.util.Map; public class RNOneSignal extends ReactContextBaseJavaModule implements IPushSubscriptionObserver, @@ -739,4 +735,10 @@ public void addListener(String eventName) { public void removeListeners(int count) { // Keep: Required for RN built in Event Emitter Calls. } + + + @ReactMethod + public void trackEvent(String name, @Nullable ReadableMap properties) { + OneSignal.getUser().trackEvent(name, properties != null ? RNUtils.convertReadableMapToMap(properties) : null); + } } diff --git a/android/src/main/java/com/onesignal/rnonesignalandroid/RNUtils.java b/android/src/main/java/com/onesignal/rnonesignalandroid/RNUtils.java index e8e55f10..79384787 100644 --- a/android/src/main/java/com/onesignal/rnonesignalandroid/RNUtils.java +++ b/android/src/main/java/com/onesignal/rnonesignalandroid/RNUtils.java @@ -284,6 +284,52 @@ public static HashMap convertReadableMapIntoStringMap(ReadableMa return stringMap; } + public static Map convertReadableMapToMap(ReadableMap readableMap) { + Map map = new HashMap<>(); + ReadableMapKeySetIterator iterator = readableMap.keySetIterator(); + + while (iterator.hasNextKey()) { + String key = iterator.nextKey(); + ReadableType type = readableMap.getType(key); + map.put(key, convertValue(type, readableMap, key)); + } + + return map; + } + + public static List convertReadableArrayToList(ReadableArray readableArray) { + List list = new ArrayList<>(); + + for (int i = 0; i < readableArray.size(); i++) { + ReadableType type = readableArray.getType(i); + list.add(convertValue(type, readableArray, i)); + } + + return list; + } + + private static Object convertValue(ReadableType type, ReadableMap map, String key) { + switch (type) { + case Boolean: return map.getBoolean(key); + case Number: return map.getDouble(key); + case String: return map.getString(key); + case Map: return convertReadableMapToMap(map.getMap(key)); + case Array: return convertReadableArrayToList(map.getArray(key)); + default: return null; + } + } + + private static Object convertValue(ReadableType type, ReadableArray array, int index) { + switch (type) { + case Boolean: return array.getBoolean(index); + case Number: return array.getDouble(index); + case String: return array.getString(index); + case Map: return convertReadableMapToMap(array.getMap(index)); + case Array: return convertReadableArrayToList(array.getArray(index)); + default: return null; + } + } + public static HashMap convertPermissionToMap(boolean granted) { HashMap hash = new HashMap<>(); diff --git a/examples/RNOneSignalTS/OSButtons.tsx b/examples/RNOneSignalTS/OSButtons.tsx index 6324ffb4..f1e0214b 100644 --- a/examples/RNOneSignalTS/OSButtons.tsx +++ b/examples/RNOneSignalTS/OSButtons.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { StyleSheet, Text, View } from 'react-native'; +import { Platform, StyleSheet, Text, View } from 'react-native'; import { OneSignal } from 'react-native-onesignal'; import { renderButtonView } from './Helpers'; // Remove: import {Text, Divider} from '@react-native-material/core'; @@ -431,11 +431,33 @@ class OSButtons extends React.Component { }, ); + const trackEventButton = renderButtonView('Track Event', () => { + loggingFunction('Tracking event: ', 'ReactNative'); + const platform = Platform.OS; // This will be 'ios' or 'android' + OneSignal.User.trackEvent(`ReactNative-${platform}-noprops`); + OneSignal.User.trackEvent(`ReactNative-${platform}`, { + someNum: 123, + someFloat: 3.14159, + someString: 'abc', + someBool: true, + someObject: { + abc: '123', + nested: { + def: '456', + }, + }, + someArray: [1, 2, 3, 4], + someMixedArray: [1, '2', { abc: '123' }], + someNull: null, + }); + }); + return [ loginButton, logoutButton, addEmailButton, removeEmailButton, + trackEventButton, sendTagWithKeyButton, deleteTagWithKeyButton, addTagsButton, diff --git a/examples/RNOneSignalTS/OSDemo.tsx b/examples/RNOneSignalTS/OSDemo.tsx index d532c248..07e2daf6 100644 --- a/examples/RNOneSignalTS/OSDemo.tsx +++ b/examples/RNOneSignalTS/OSDemo.tsx @@ -38,7 +38,7 @@ class OSDemo extends React.Component { async componentDidMount() { OneSignal.initialize(APP_ID); - OneSignal.Debug.setLogLevel(LogLevel.None); + OneSignal.Debug.setLogLevel(LogLevel.Verbose); OneSignal.LiveActivities.setupDefault(); // OneSignal.LiveActivities.setupDefault({ diff --git a/examples/RNOneSignalTS/ios/Podfile.lock b/examples/RNOneSignalTS/ios/Podfile.lock index caa50337..0270201d 100644 --- a/examples/RNOneSignalTS/ios/Podfile.lock +++ b/examples/RNOneSignalTS/ios/Podfile.lock @@ -8,9 +8,9 @@ PODS: - hermes-engine (0.81.4): - hermes-engine/Pre-built (= 0.81.4) - hermes-engine/Pre-built (0.81.4) - - OneSignalXCFramework (5.2.14): - - OneSignalXCFramework/OneSignalComplete (= 5.2.14) - - OneSignalXCFramework/OneSignal (5.2.14): + - OneSignalXCFramework (5.4.0-alpha-01): + - OneSignalXCFramework/OneSignalComplete (= 5.4.0-alpha-01) + - OneSignalXCFramework/OneSignal (5.4.0-alpha-01): - OneSignalXCFramework/OneSignalCore - OneSignalXCFramework/OneSignalExtension - OneSignalXCFramework/OneSignalLiveActivities @@ -18,38 +18,38 @@ PODS: - OneSignalXCFramework/OneSignalOSCore - OneSignalXCFramework/OneSignalOutcomes - OneSignalXCFramework/OneSignalUser - - OneSignalXCFramework/OneSignalComplete (5.2.14): + - OneSignalXCFramework/OneSignalComplete (5.4.0-alpha-01): - OneSignalXCFramework/OneSignal - OneSignalXCFramework/OneSignalInAppMessages - OneSignalXCFramework/OneSignalLocation - - OneSignalXCFramework/OneSignalCore (5.2.14) - - OneSignalXCFramework/OneSignalExtension (5.2.14): + - OneSignalXCFramework/OneSignalCore (5.4.0-alpha-01) + - OneSignalXCFramework/OneSignalExtension (5.4.0-alpha-01): - OneSignalXCFramework/OneSignalCore - OneSignalXCFramework/OneSignalOutcomes - - OneSignalXCFramework/OneSignalInAppMessages (5.2.14): + - OneSignalXCFramework/OneSignalInAppMessages (5.4.0-alpha-01): - OneSignalXCFramework/OneSignalCore - OneSignalXCFramework/OneSignalNotifications - OneSignalXCFramework/OneSignalOSCore - OneSignalXCFramework/OneSignalOutcomes - OneSignalXCFramework/OneSignalUser - - OneSignalXCFramework/OneSignalLiveActivities (5.2.14): + - OneSignalXCFramework/OneSignalLiveActivities (5.4.0-alpha-01): - OneSignalXCFramework/OneSignalCore - OneSignalXCFramework/OneSignalOSCore - OneSignalXCFramework/OneSignalUser - - OneSignalXCFramework/OneSignalLocation (5.2.14): + - OneSignalXCFramework/OneSignalLocation (5.4.0-alpha-01): - OneSignalXCFramework/OneSignalCore - OneSignalXCFramework/OneSignalNotifications - OneSignalXCFramework/OneSignalOSCore - OneSignalXCFramework/OneSignalUser - - OneSignalXCFramework/OneSignalNotifications (5.2.14): + - OneSignalXCFramework/OneSignalNotifications (5.4.0-alpha-01): - OneSignalXCFramework/OneSignalCore - OneSignalXCFramework/OneSignalExtension - OneSignalXCFramework/OneSignalOutcomes - - OneSignalXCFramework/OneSignalOSCore (5.2.14): + - OneSignalXCFramework/OneSignalOSCore (5.4.0-alpha-01): - OneSignalXCFramework/OneSignalCore - - OneSignalXCFramework/OneSignalOutcomes (5.2.14): + - OneSignalXCFramework/OneSignalOutcomes (5.4.0-alpha-01): - OneSignalXCFramework/OneSignalCore - - OneSignalXCFramework/OneSignalUser (5.2.14): + - OneSignalXCFramework/OneSignalUser (5.4.0-alpha-01): - OneSignalXCFramework/OneSignalCore - OneSignalXCFramework/OneSignalNotifications - OneSignalXCFramework/OneSignalOSCore @@ -1795,7 +1795,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - react-native-onesignal (5.2.13): - - OneSignalXCFramework (= 5.2.14) + - OneSignalXCFramework (= 5.4.0-alpha-01) - React (< 1.0.0, >= 0.13.0) - react-native-safe-area-context (5.6.1): - boost @@ -2631,7 +2631,7 @@ SPEC CHECKSUMS: fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd glog: 5683914934d5b6e4240e497e0f4a3b42d1854183 hermes-engine: 35c763d57c9832d0eef764316ca1c4d043581394 - OneSignalXCFramework: 7112f3e89563e41ebc23fe807788f11985ac541c + OneSignalXCFramework: 689af4166ff7fb10349f0bbf93df75872fef1f8d RCT-Folly: 846fda9475e61ec7bcbf8a3fe81edfcaeb090669 RCTDeprecation: c0ed3249a97243002615517dff789bf4666cf585 RCTRequired: 58719f5124f9267b5f9649c08bf23d9aea845b23 @@ -2665,7 +2665,7 @@ SPEC CHECKSUMS: React-logger: a3cb5b29c32b8e447b5a96919340e89334062b48 React-Mapbuffer: 9d2434a42701d6144ca18f0ca1c4507808ca7696 React-microtasksnativemodule: 75b6604b667d297292345302cc5bfb6b6aeccc1b - react-native-onesignal: 0829ce2a504db26dd80eaf14d4e0211ea626fe62 + react-native-onesignal: 372119089f89c208e3a68586e9f3c24f16d599da react-native-safe-area-context: c6e2edd1c1da07bdce287fa9d9e60c5f7b514616 React-NativeModulesApple: 879fbdc5dcff7136abceb7880fe8a2022a1bd7c3 React-oscompat: 93b5535ea7f7dff46aaee4f78309a70979bdde9d diff --git a/examples/RNOneSignalTS/ios/RNOneSignalTS.xcodeproj/project.pbxproj b/examples/RNOneSignalTS/ios/RNOneSignalTS.xcodeproj/project.pbxproj index 20907fb2..b1e6c3fd 100644 --- a/examples/RNOneSignalTS/ios/RNOneSignalTS.xcodeproj/project.pbxproj +++ b/examples/RNOneSignalTS/ios/RNOneSignalTS.xcodeproj/project.pbxproj @@ -193,14 +193,10 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-RNOneSignalTS/Pods-RNOneSignalTS-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - ); name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-RNOneSignalTS/Pods-RNOneSignalTS-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); - outputPaths = ( - ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RNOneSignalTS/Pods-RNOneSignalTS-frameworks.sh\"\n"; @@ -236,14 +232,10 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-RNOneSignalTS/Pods-RNOneSignalTS-resources-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - ); name = "[CP] Copy Pods Resources"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-RNOneSignalTS/Pods-RNOneSignalTS-resources-${CONFIGURATION}-output-files.xcfilelist", ); - outputPaths = ( - ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RNOneSignalTS/Pods-RNOneSignalTS-resources.sh\"\n"; diff --git a/ios/RCTOneSignal/RCTOneSignalEventEmitter.m b/ios/RCTOneSignal/RCTOneSignalEventEmitter.m index 50bc26a4..4a1d0782 100644 --- a/ios/RCTOneSignal/RCTOneSignalEventEmitter.m +++ b/ios/RCTOneSignal/RCTOneSignalEventEmitter.m @@ -528,4 +528,8 @@ + (void)sendEventWithName:(NSString *)name withBody:(NSDictionary *)body { // iOS Stub } +RCT_EXPORT_METHOD(trackEvent:(NSString *)name withProperties:(NSDictionary * _Nullable)properties) { + [OneSignal.User trackEventWithName:name properties:properties]; +} + @end diff --git a/react-native-onesignal.podspec b/react-native-onesignal.podspec index 66448043..b1b30fbe 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.2.14' + s.dependency 'OneSignalXCFramework', '5.4.0-alpha-01' end diff --git a/src/index.ts b/src/index.ts index 09b8011e..f8f4a289 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,30 +14,30 @@ import { SUBSCRIPTION_CHANGED, USER_STATE_CHANGED, } from './events/events'; +import NotificationWillDisplayEvent from './events/NotificationWillDisplayEvent'; +import { isNativeModuleLoaded, isValidCallback } from './helpers'; import { + InAppMessage, + InAppMessageClickEvent, + InAppMessageDidDismissEvent, + InAppMessageDidDisplayEvent, + InAppMessageEventName, + InAppMessageEventTypeMap, + InAppMessageWillDismissEvent, + InAppMessageWillDisplayEvent, +} from './models/InAppMessage'; +import { LiveActivitySetupOptions } from './models/LiveActivities'; +import { + NotificationClickEvent, NotificationEventName, NotificationEventTypeMap, - NotificationClickEvent, } from './models/NotificationEvents'; import { - PushSubscriptionState, OSNotificationPermission, PushSubscriptionChangedState, + PushSubscriptionState, } from './models/Subscription'; -import { UserState, UserChangedState } from './models/User'; -import NotificationWillDisplayEvent from './events/NotificationWillDisplayEvent'; -import { LiveActivitySetupOptions } from './models/LiveActivities'; -import { - InAppMessage, - InAppMessageEventTypeMap, - InAppMessageEventName, - InAppMessageClickEvent, - InAppMessageWillDisplayEvent, - InAppMessageDidDisplayEvent, - InAppMessageWillDismissEvent, - InAppMessageDidDismissEvent, -} from './models/InAppMessage'; -import { isValidCallback, isNativeModuleLoaded } from './helpers'; +import { UserChangedState, UserState } from './models/User'; const RNOneSignal = NativeModules.OneSignal; const eventManager = new EventManager(RNOneSignal); @@ -605,6 +605,21 @@ export namespace OneSignal { return RNOneSignal.getTags(); } + + /** Track custom events for the current user. */ + export function trackEvent( + name: string, + properties: Record = {}, + ) { + if (!isNativeModuleLoaded(RNOneSignal)) return; + + if (!isObjectSerializable(properties)) { + console.error('Properties must be JSON-serializable'); + return; + } + + RNOneSignal.trackEvent(name, properties); + } } export namespace Notifications { @@ -1000,22 +1015,37 @@ export namespace OneSignal { } } +/** + * Returns true if the value is a JSON-serializable object. + */ +function isObjectSerializable(value: unknown): boolean { + if (typeof value !== 'object' || value === null || Array.isArray(value)) { + return false; + } + try { + JSON.stringify(value); + return true; + } catch (e) { + return false; + } +} + export { - NotificationWillDisplayEvent, - NotificationClickEvent, InAppMessage, InAppMessageClickEvent, - InAppMessageWillDisplayEvent, + InAppMessageDidDismissEvent, InAppMessageDidDisplayEvent, InAppMessageWillDismissEvent, - InAppMessageDidDismissEvent, - PushSubscriptionState, + InAppMessageWillDisplayEvent, + NotificationClickEvent, + NotificationWillDisplayEvent, + OSNotificationPermission, PushSubscriptionChangedState, - UserState, + PushSubscriptionState, UserChangedState, - OSNotificationPermission, + UserState, }; -export { default as OSNotification } from './OSNotification'; -export { NotificationClickResult } from './models/NotificationEvents'; export { InAppMessageClickResult } from './models/InAppMessage'; +export { NotificationClickResult } from './models/NotificationEvents'; +export { default as OSNotification } from './OSNotification';