Skip to content

Commit 7e850a9

Browse files
fadi-georgegithub-actions[bot]
andauthored
feat: add custom events support (#1903)
Co-authored-by: github-actions[bot] <noreply@onesignal.com>
1 parent 145c7b5 commit 7e850a9

12 files changed

Lines changed: 183 additions & 23 deletions

File tree

__mocks__/react-native.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ const mockRNOneSignal = {
8686
addOutcomeWithValue: vi.fn(),
8787
displayNotification: vi.fn(),
8888
preventDefault: vi.fn(),
89+
trackEvent: vi.fn(),
8990
};
9091

9192
const mockPlatform = {

android/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ dependencies {
3131

3232
// api is used instead of implementation so the parent :app project can access any of the OneSignal Java
3333
// classes if needed. Such as com.onesignal.NotificationExtenderService
34-
api 'com.onesignal:OneSignal:5.4.2'
34+
api 'com.onesignal:OneSignal:5.6.1'
3535

3636
testImplementation 'junit:junit:4.12'
3737
}

android/src/main/java/com/onesignal/rnonesignalandroid/RNOneSignal.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ of this software and associated documentation files (the "Software"), to deal
3636
package com.onesignal.rnonesignalandroid;
3737

3838
import android.content.Context;
39+
import androidx.annotation.Nullable;
3940
import com.facebook.react.bridge.Arguments;
4041
import com.facebook.react.bridge.LifecycleEventListener;
4142
import com.facebook.react.bridge.Promise;
@@ -754,4 +755,9 @@ public void addListener(String eventName) {
754755
public void removeListeners(int count) {
755756
// Keep: Required for RN built in Event Emitter Calls.
756757
}
758+
759+
@ReactMethod
760+
public void trackEvent(String name, @Nullable ReadableMap properties) {
761+
OneSignal.getUser().trackEvent(name, properties != null ? properties.toHashMap() : new HashMap<>());
762+
}
757763
}

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ spotless {
1919
java {
2020
target 'android/**/*.java', 'examples/RNOneSignalTS/android/app/src/**/*.java'
2121
targetExclude '**/build/**'
22-
palantirJavaFormat('2.28.0')
22+
palantirJavaFormat('2.85.0')
2323
removeUnusedImports()
2424
trimTrailingWhitespace()
2525
endWithNewline()

examples/RNOneSignalTS/OSButtons.tsx

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from 'react';
2-
import { Text, View } from 'react-native';
2+
import { Platform, Text, View } from 'react-native';
33
import { OneSignal } from 'react-native-onesignal';
44
import { renderButtonView } from './Helpers';
55
// Remove: import {Text, Divider} from '@react-native-material/core';
@@ -396,11 +396,34 @@ const OSButtons: React.FC<Props> = ({ loggingFunction, inputFieldValue }) => {
396396
},
397397
);
398398

399+
const trackEventButton = renderButtonView('Track Event', () => {
400+
loggingFunction('Tracking event: ', 'ReactNative');
401+
const platform = Platform.OS; // This will be 'ios' or 'android'
402+
OneSignal.User.trackEvent(`ReactNative-${platform}-noprops`);
403+
OneSignal.User.trackEvent(`ReactNative-${platform}`, {
404+
someNum: 123,
405+
someFloat: 3.14159,
406+
someString: 'abc',
407+
someBool: true,
408+
someObject: {
409+
abc: '123',
410+
nested: {
411+
def: '456',
412+
},
413+
ghi: null,
414+
},
415+
someArray: [1, 2],
416+
someMixedArray: [1, '2', { abc: '123' }, null],
417+
someNull: null,
418+
});
419+
});
420+
399421
return [
400422
loginButton,
401423
logoutButton,
402424
addEmailButton,
403425
removeEmailButton,
426+
trackEventButton,
404427
sendTagWithKeyButton,
405428
deleteTagWithKeyButton,
406429
addTagsButton,

examples/RNOneSignalTS/ios/Podfile.lock

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,48 +8,48 @@ PODS:
88
- hermes-engine (0.82.1):
99
- hermes-engine/Pre-built (= 0.82.1)
1010
- hermes-engine/Pre-built (0.82.1)
11-
- OneSignalXCFramework (5.2.16):
12-
- OneSignalXCFramework/OneSignalComplete (= 5.2.16)
13-
- OneSignalXCFramework/OneSignal (5.2.16):
11+
- OneSignalXCFramework (5.4.0):
12+
- OneSignalXCFramework/OneSignalComplete (= 5.4.0)
13+
- OneSignalXCFramework/OneSignal (5.4.0):
1414
- OneSignalXCFramework/OneSignalCore
1515
- OneSignalXCFramework/OneSignalExtension
1616
- OneSignalXCFramework/OneSignalLiveActivities
1717
- OneSignalXCFramework/OneSignalNotifications
1818
- OneSignalXCFramework/OneSignalOSCore
1919
- OneSignalXCFramework/OneSignalOutcomes
2020
- OneSignalXCFramework/OneSignalUser
21-
- OneSignalXCFramework/OneSignalComplete (5.2.16):
21+
- OneSignalXCFramework/OneSignalComplete (5.4.0):
2222
- OneSignalXCFramework/OneSignal
2323
- OneSignalXCFramework/OneSignalInAppMessages
2424
- OneSignalXCFramework/OneSignalLocation
25-
- OneSignalXCFramework/OneSignalCore (5.2.16)
26-
- OneSignalXCFramework/OneSignalExtension (5.2.16):
25+
- OneSignalXCFramework/OneSignalCore (5.4.0)
26+
- OneSignalXCFramework/OneSignalExtension (5.4.0):
2727
- OneSignalXCFramework/OneSignalCore
2828
- OneSignalXCFramework/OneSignalOutcomes
29-
- OneSignalXCFramework/OneSignalInAppMessages (5.2.16):
29+
- OneSignalXCFramework/OneSignalInAppMessages (5.4.0):
3030
- OneSignalXCFramework/OneSignalCore
3131
- OneSignalXCFramework/OneSignalNotifications
3232
- OneSignalXCFramework/OneSignalOSCore
3333
- OneSignalXCFramework/OneSignalOutcomes
3434
- OneSignalXCFramework/OneSignalUser
35-
- OneSignalXCFramework/OneSignalLiveActivities (5.2.16):
35+
- OneSignalXCFramework/OneSignalLiveActivities (5.4.0):
3636
- OneSignalXCFramework/OneSignalCore
3737
- OneSignalXCFramework/OneSignalOSCore
3838
- OneSignalXCFramework/OneSignalUser
39-
- OneSignalXCFramework/OneSignalLocation (5.2.16):
39+
- OneSignalXCFramework/OneSignalLocation (5.4.0):
4040
- OneSignalXCFramework/OneSignalCore
4141
- OneSignalXCFramework/OneSignalNotifications
4242
- OneSignalXCFramework/OneSignalOSCore
4343
- OneSignalXCFramework/OneSignalUser
44-
- OneSignalXCFramework/OneSignalNotifications (5.2.16):
44+
- OneSignalXCFramework/OneSignalNotifications (5.4.0):
4545
- OneSignalXCFramework/OneSignalCore
4646
- OneSignalXCFramework/OneSignalExtension
4747
- OneSignalXCFramework/OneSignalOutcomes
48-
- OneSignalXCFramework/OneSignalOSCore (5.2.16):
48+
- OneSignalXCFramework/OneSignalOSCore (5.4.0):
4949
- OneSignalXCFramework/OneSignalCore
50-
- OneSignalXCFramework/OneSignalOutcomes (5.2.16):
50+
- OneSignalXCFramework/OneSignalOutcomes (5.4.0):
5151
- OneSignalXCFramework/OneSignalCore
52-
- OneSignalXCFramework/OneSignalUser (5.2.16):
52+
- OneSignalXCFramework/OneSignalUser (5.4.0):
5353
- OneSignalXCFramework/OneSignalCore
5454
- OneSignalXCFramework/OneSignalNotifications
5555
- OneSignalXCFramework/OneSignalOSCore
@@ -1828,8 +1828,8 @@ PODS:
18281828
- React-RCTFBReactNativeSpec
18291829
- ReactCommon/turbomodule/core
18301830
- SocketRocket
1831-
- react-native-onesignal (5.2.17):
1832-
- OneSignalXCFramework (= 5.2.16)
1831+
- react-native-onesignal (5.3.0):
1832+
- OneSignalXCFramework (= 5.4.0)
18331833
- React (< 1.0.0, >= 0.13.0)
18341834
- react-native-safe-area-context (5.6.2):
18351835
- boost
@@ -2768,7 +2768,7 @@ SPEC CHECKSUMS:
27682768
fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd
27692769
glog: 5683914934d5b6e4240e497e0f4a3b42d1854183
27702770
hermes-engine: 273e30e7fb618279934b0b95ffab60ecedb7acf5
2771-
OneSignalXCFramework: 8ed6648481bee0bd973a138fecd80331b798524f
2771+
OneSignalXCFramework: 95b6391df5a91b448003149c1a633ade42ceca1e
27722772
RCT-Folly: 846fda9475e61ec7bcbf8a3fe81edfcaeb090669
27732773
RCTDeprecation: f17e2ebc07876ca9ab8eb6e4b0a4e4647497ae3a
27742774
RCTRequired: e2c574c1b45231f7efb0834936bd609d75072b63
@@ -2802,7 +2802,7 @@ SPEC CHECKSUMS:
28022802
React-logger: 500f2fa5697d224e63c33d913c8a4765319e19bf
28032803
React-Mapbuffer: 06d59c448da7e34eb05b3fb2189e12f6a30fec57
28042804
React-microtasksnativemodule: d1ee999dc9052e23f6488b730fa2d383a4ea40e5
2805-
react-native-onesignal: 3b6cd199ec0db87166ef7fb595715627a35b3244
2805+
react-native-onesignal: 68c8423063cc8ead827e09bc71d139c14850feaf
28062806
react-native-safe-area-context: c00143b4823773bba23f2f19f85663ae89ceb460
28072807
React-NativeModulesApple: 46690a0fe94ec28fc6fc686ec797b911d251ded0
28082808
React-oscompat: 95875e81f5d4b3c7b2c888d5bd2c9d83450d8bdb

ios/RCTOneSignal/RCTOneSignalEventEmitter.m

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -628,4 +628,9 @@ - (void)removeUserStateObserver {
628628
}
629629
}
630630

631+
RCT_EXPORT_METHOD(trackEvent : (NSString *)name withProperties : (
632+
NSDictionary *_Nullable)properties) {
633+
[OneSignal.User trackEventWithName:name properties:properties];
634+
}
635+
631636
@end

react-native-onesignal.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,5 @@ Pod::Spec.new do |s|
2222
# pod 'React', :path => '../node_modules/react-native/'
2323

2424
# The Native OneSignal-iOS-SDK XCFramework from cocoapods.
25-
s.dependency 'OneSignalXCFramework', '5.2.16'
25+
s.dependency 'OneSignalXCFramework', '5.4.0'
2626
end

src/helpers.test.ts

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import type { NativeModule } from 'react-native';
22
import type { MockInstance } from 'vitest';
3-
import { isNativeModuleLoaded, isValidCallback } from './helpers';
3+
import {
4+
isNativeModuleLoaded,
5+
isObjectSerializable,
6+
isValidCallback,
7+
} from './helpers';
48

59
describe('helpers', () => {
610
let errorSpy: MockInstance;
@@ -58,4 +62,42 @@ describe('helpers', () => {
5862
expect(result).toBe(true);
5963
});
6064
});
65+
66+
describe('isObjectSerializable', () => {
67+
test.each([
68+
{ description: 'an empty object', value: {} },
69+
{ description: 'an object with string values', value: { key: 'value' } },
70+
{ description: 'an object with number values', value: { count: 42 } },
71+
{ description: 'an object with boolean values', value: { active: true } },
72+
{ description: 'an object with null values', value: { data: null } },
73+
{
74+
description: 'a nested object',
75+
value: { outer: { inner: 'value' } },
76+
},
77+
{
78+
description: 'an object with array values',
79+
value: { items: [1, 2, 3] },
80+
},
81+
])('should return true for $description', ({ value }) => {
82+
expect(isObjectSerializable(value)).toBe(true);
83+
});
84+
85+
test.each([
86+
{ description: 'null', value: null },
87+
{ description: 'undefined', value: undefined },
88+
{ description: 'a string', value: 'string' },
89+
{ description: 'a number', value: 123 },
90+
{ description: 'a boolean', value: true },
91+
{ description: 'an array', value: [1, 2, 3] },
92+
{ description: 'a function', value: () => {} },
93+
])('should return false for $description', ({ value }) => {
94+
expect(isObjectSerializable(value)).toBe(false);
95+
});
96+
97+
test('should return false for objects with circular references', () => {
98+
const circular: Record<string, unknown> = {};
99+
circular.self = circular;
100+
expect(isObjectSerializable(circular)).toBe(false);
101+
});
102+
});
61103
});

src/helpers.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,18 @@ export function isNativeModuleLoaded(module: NativeModule): boolean {
1616

1717
return true;
1818
}
19+
20+
/**
21+
* Returns true if the value is a JSON-serializable object.
22+
*/
23+
export function isObjectSerializable(value: unknown): boolean {
24+
if (!(typeof value === 'object' && value !== null && !Array.isArray(value))) {
25+
return false;
26+
}
27+
try {
28+
JSON.stringify(value);
29+
return true;
30+
} catch {
31+
return false;
32+
}
33+
}

0 commit comments

Comments
 (0)