Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,5 @@ dependencies {
}

testImplementation 'junit:junit:4.12'
testImplementation 'org.json:json:20240303'
}
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,21 @@ public static HashMap<String, Object> convertNotificationToMap(INotification not
}
notificationHash.put("actionButtons", actionButtons);
}
notificationHash.put("rawPayload", notification.getRawPayload());
notificationHash.put("rawPayload", convertRawPayloadToHashMap(notification.getRawPayload()));

return notificationHash;
}

static HashMap<String, Object> convertRawPayloadToHashMap(String rawPayload) {
if (rawPayload == null || rawPayload.trim().isEmpty()) return new HashMap<>();

try {
return convertJSONObjectToHashMap(new JSONObject(rawPayload));
} catch (JSONException exception) {
Comment thread
fadi-george marked this conversation as resolved.
Resolved
return new HashMap<>();
}
}

private static HashMap<String, Object> convertInAppMessageToMap(IInAppMessage message) {
HashMap<String, Object> hash = new HashMap<>();
hash.put("messageId", message.getMessageId());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.onesignal.rnonesignalandroid;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.util.HashMap;
import java.util.List;
import org.junit.Test;

public class RNUtilsTest {
@Test
public void convertRawPayloadToHashMapConvertsNestedJson() {
HashMap<String, Object> payload = RNUtils.convertRawPayloadToHashMap(
"{\"message\":\"hello\",\"count\":2,\"enabled\":true,\"nested\":{\"value\":\"inside\"},"
+ "\"items\":[1,{\"name\":\"item\"}]}");

assertEquals("hello", payload.get("message"));
assertEquals(2, payload.get("count"));
assertEquals(true, payload.get("enabled"));

HashMap<String, Object> nested = (HashMap<String, Object>) payload.get("nested");
assertEquals("inside", nested.get("value"));

List<Object> items = (List<Object>) payload.get("items");
assertEquals(1, items.get(0));
assertEquals("item", ((HashMap<String, Object>) items.get(1)).get("name"));
}

@Test
public void convertRawPayloadToHashMapReturnsEmptyMapForEmptyPayload() {
assertTrue(RNUtils.convertRawPayloadToHashMap("").isEmpty());
assertTrue(RNUtils.convertRawPayloadToHashMap(" ").isEmpty());
assertTrue(RNUtils.convertRawPayloadToHashMap(null).isEmpty());
}

@Test
public void convertRawPayloadToHashMapReturnsEmptyMapForInvalidPayload() {
assertTrue(RNUtils.convertRawPayloadToHashMap("not-json").isEmpty());
assertTrue(RNUtils.convertRawPayloadToHashMap("[1, 2]").isEmpty());
}
}
12 changes: 12 additions & 0 deletions examples/demo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,18 @@ When you want to forcefully reload, for example to reset the state of your app,
- **Android**: Press the <kbd>R</kbd> key twice or select **"Reload"** from the **Dev Menu**, accessed via <kbd>Ctrl</kbd> + <kbd>M</kbd> (Windows/Linux) or <kbd>Cmd ⌘</kbd> + <kbd>M</kbd> (macOS).
- **iOS**: Press <kbd>R</kbd> in iOS Simulator.

## Testing iOS Action Button Icons

The iOS demo bundles shared action button icons in both the `demo` and
`OneSignalNotificationServiceExtension` targets. Create an image notification
in the OneSignal dashboard with these action buttons:

- **Save Story** with icon `template-bookmark-icon`
- **Share** with icon `template-share-icon`

Enter the icon names without the `.png` extension. On an iOS 15 or newer
physical device, expand the notification to verify that both icons render.

## Congratulations! :tada:

You've successfully run and modified your React Native App. :partying_face:
Expand Down
2 changes: 1 addition & 1 deletion examples/demo/bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions examples/demo/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2314,7 +2314,7 @@ EXTERNAL SOURCES:

SPEC CHECKSUMS:
FBLazyVector: c12d2108050e27952983d565a232f6f7b1ad5e69
hermes-engine: 431876dbea22ba6821b0e999131ecbf5d706c8d2
hermes-engine: 177322198264d50dc281084c48e1ed80ea14884c
Comment thread
fadi-george marked this conversation as resolved.
Resolved
OneSignalXCFramework: c38d48069949912d19e7c64d2ff30f5c9edba1ee
RCTDeprecation: 3280799c14232a56e5a44f92981a8ee33bc69fd9
RCTRequired: 9854a51b0f65ccf43ea0b744df4d70fce339db32
Expand All @@ -2324,7 +2324,7 @@ SPEC CHECKSUMS:
React: 7ef36630d07638043a134a7dd2ec17e0be10fc3c
React-callinvoker: af4e8fe1d60ab63dd8d74c2a68988064c2848954
React-Core: c609976c034ba9556bef9850a571a71bd458d73f
React-Core-prebuilt: 5e3e0e5fe0a22af7fb36c5b593882da759f395c9
React-Core-prebuilt: f4df078355bf9605a0d0278aa5c63fcd4b764a1d
React-CoreModules: 0ea85f3b3f4b8cbfb3afacd2ed85458fb878517a
React-cxxreact: 6752bab77c0599d6136e2b8b9b64b4a7d316d401
React-debug: 38389b86e3570558ec73dd4cbc0cd2f2eec47a51
Expand Down Expand Up @@ -2387,7 +2387,7 @@ SPEC CHECKSUMS:
ReactAppDependencyProvider: 6b7e8d8d974ed13fb66698d82c30c5e70c1f7d3a
ReactCodegen: c5e5343f6691b0cd76913b9be5e89e5a83ff3315
ReactCommon: 92b53b0bd7f7d86154dc9f512c1ea5dee717cc72
ReactNativeDependencies: f822e1f0812f671d9bfe99225d09dc144fa6f02b
ReactNativeDependencies: eaebe8c9533ec2c41b08a02844af461bfff10540
RNCAsyncStorage: 3a4f5e2777dae1688b781a487923a08569e27fe4
RNScreens: 6cb648bdad8fe9bee9259fe144df95b6d1d5b707
RNSVG: c69f7709226108f5eb89b5aa8833c17a36345468
Expand Down
12 changes: 12 additions & 0 deletions examples/demo/ios/demo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
A10000000000000000000007 /* libPods-OneSignalWidgetExtension.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A20000000000000000000010 /* libPods-OneSignalWidgetExtension.a */; };
A10000000000000000000008 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A20000000000000000000009 /* WidgetKit.framework */; };
A10000000000000000000009 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A2000000000000000000000A /* SwiftUI.framework */; };
B10000000000000000000001 /* template-bookmark-icon.png in Resources */ = {isa = PBXBuildFile; fileRef = B20000000000000000000001 /* template-bookmark-icon.png */; };
B10000000000000000000002 /* template-share-icon.png in Resources */ = {isa = PBXBuildFile; fileRef = B20000000000000000000002 /* template-share-icon.png */; };
B10000000000000000000003 /* template-bookmark-icon.png in Resources */ = {isa = PBXBuildFile; fileRef = B20000000000000000000001 /* template-bookmark-icon.png */; };
B10000000000000000000004 /* template-share-icon.png in Resources */ = {isa = PBXBuildFile; fileRef = B20000000000000000000002 /* template-share-icon.png */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -82,6 +86,8 @@
A2000000000000000000000F /* libPods-OneSignalNotificationServiceExtension.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-OneSignalNotificationServiceExtension.a"; sourceTree = BUILT_PRODUCTS_DIR; };
A20000000000000000000010 /* libPods-OneSignalWidgetExtension.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-OneSignalWidgetExtension.a"; sourceTree = BUILT_PRODUCTS_DIR; };
A20000000000000000000011 /* demo.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = demo.entitlements; path = demo/demo.entitlements; sourceTree = "<group>"; };
B20000000000000000000001 /* template-bookmark-icon.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "template-bookmark-icon.png"; path = "demo/template-bookmark-icon.png"; sourceTree = "<group>"; };
B20000000000000000000002 /* template-share-icon.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "template-share-icon.png"; path = "demo/template-share-icon.png"; sourceTree = "<group>"; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
/* End PBXFileReference section */

Expand Down Expand Up @@ -124,6 +130,8 @@
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */,
13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */,
A20000000000000000000011 /* demo.entitlements */,
B20000000000000000000001 /* template-bookmark-icon.png */,
B20000000000000000000002 /* template-share-icon.png */,
);
name = demo;
sourceTree = "<group>";
Expand Down Expand Up @@ -317,13 +325,17 @@
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
5F8F7D5C84F8947D278D5B06 /* PrivacyInfo.xcprivacy in Resources */,
B10000000000000000000001 /* template-bookmark-icon.png in Resources */,
B10000000000000000000002 /* template-share-icon.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
A70000000000000000000001 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B10000000000000000000003 /* template-bookmark-icon.png in Resources */,
B10000000000000000000004 /* template-share-icon.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
Binary file added examples/demo/ios/demo/template-bookmark-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/demo/ios/demo/template-share-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 20 additions & 0 deletions examples/demo/src/hooks/useOneSignal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,15 +191,35 @@ function useOneSignalState(): UseOneSignalReturn {

const handleNotificationClick = (e: NotificationClickEvent) => {
console.log(`Notification click: ${e.notification.title ?? ''}`);

// uncomment to see the full event object
// console.log('[OneSignal] click event: ', e);
};

const handleForegroundWillDisplay = (e: NotificationWillDisplayEvent) => {
console.log(`Notification foregroundWillDisplay: ${e.getNotification().title ?? ''}`);

// uncomment to see the full event object
// console.log('[OneSignal] will display event: ', e.getNotification());

// uncomment to test preventing the default display behavior
// e.preventDefault();

// can call this after preventDefault (within ~25 seconds) to force display of notification
// e.getNotification().display();

// example with a delay (assumes preventDefault was called)
// console.log('[OneSignal] forcing display of notification in x seconds');
// let seconds = 24;
// const interval = setInterval(() => {
// seconds--;
// console.log(`[OneSignal] displaying notification in ${seconds} seconds`);
// if (seconds <= 0) {
// console.log('[OneSignal] displaying notification');
// e.getNotification().display();
// clearInterval(interval);
// }
// }, 1000);
};

const pushSubHandler = (event: PushSubscriptionChangedState) => {
Expand Down
28 changes: 4 additions & 24 deletions src/OSNotification.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ describe('OSNotification', () => {
test('should initialize with optional common properties as undefined', () => {
const notificationData = {
body: 'Test body',
rawPayload: '',
rawPayload: {},
notificationId: 'id-123',
};
const notification = new OSNotification(notificationData);
Expand Down Expand Up @@ -77,7 +77,7 @@ describe('OSNotification', () => {
collapseId: 'collapse-1',
fromProjectNumber: '123456789',
smallIconAccentColor: 'FFFF0000',
lockScreenVisibility: '1',
lockScreenVisibility: 1,
androidNotificationId: 456,
};
const notification = new OSNotification(androidData);
Expand All @@ -93,7 +93,7 @@ describe('OSNotification', () => {
expect(notification.collapseId).toBe('collapse-1');
expect(notification.fromProjectNumber).toBe('123456789');
expect(notification.smallIconAccentColor).toBe('FFFF0000');
expect(notification.lockScreenVisibility).toBe('1');
expect(notification.lockScreenVisibility).toBe(1);
expect(notification.androidNotificationId).toBe(456);
});

Expand Down Expand Up @@ -198,7 +198,7 @@ describe('OSNotification', () => {
});
});

describe('rawPayload types', () => {
describe('rawPayload', () => {
test('should accept object as rawPayload', () => {
const notificationData = {
...baseNotificationData,
Expand All @@ -212,16 +212,6 @@ describe('OSNotification', () => {
});
});

test('should accept string as rawPayload', () => {
const notificationData = {
...baseNotificationData,
rawPayload: '{"key":"value"}',
};
const notification = new OSNotification(notificationData);

expect(notification.rawPayload).toBe('{"key":"value"}');
});

test('should accept empty object as rawPayload', () => {
const notificationData = {
...baseNotificationData,
Expand All @@ -231,15 +221,5 @@ describe('OSNotification', () => {

expect(notification.rawPayload).toEqual({});
});

test('should accept empty string as rawPayload', () => {
const notificationData = {
...baseNotificationData,
rawPayload: '',
};
const notification = new OSNotification(notificationData);

expect(notification.rawPayload).toBe('');
});
});
});
8 changes: 4 additions & 4 deletions src/OSNotification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export interface BaseNotificationData {
sound?: string;
title?: string;
launchURL?: string;
rawPayload: object | string; // platform bridges return different types
rawPayload: object;
Comment thread
fadi-george marked this conversation as resolved.
Resolved
actionButtons?: object[];
additionalData?: object;
notificationId: string;
Expand All @@ -26,7 +26,7 @@ interface AndroidNotificationData extends BaseNotificationData {
collapseId?: string;
fromProjectNumber?: string;
smallIconAccentColor?: string;
lockScreenVisibility?: string;
lockScreenVisibility?: number;
androidNotificationId?: number;
}

Expand All @@ -52,7 +52,7 @@ export default class OSNotification {
sound?: string;
title?: string;
launchURL?: string;
rawPayload: object | string; // platform bridges return different types
rawPayload: object;
actionButtons?: object[];
additionalData?: object;
notificationId: string;
Expand All @@ -68,7 +68,7 @@ export default class OSNotification {
collapseId?: string;
fromProjectNumber?: string;
smallIconAccentColor?: string;
lockScreenVisibility?: string;
lockScreenVisibility?: number;
androidNotificationId?: number;
// ios only
badge?: string;
Expand Down
Loading