Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix push from closed state #880

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -192,5 +192,3 @@ package-lock.json

.history

# Typescript build
lib/dist/
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need

Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;

import com.facebook.react.bridge.ReactContext;
import com.wix.reactnativenotifications.core.AppLaunchHelper;
Expand All @@ -18,6 +19,7 @@
import com.wix.reactnativenotifications.core.JsIOHelper;
import com.wix.reactnativenotifications.core.NotificationIntentAdapter;

import static com.wix.reactnativenotifications.Defs.LOGTAG;
import static com.wix.reactnativenotifications.Defs.NOTIFICATION_OPENED_EVENT_NAME;
import static com.wix.reactnativenotifications.Defs.NOTIFICATION_RECEIVED_EVENT_NAME;
import static com.wix.reactnativenotifications.Defs.NOTIFICATION_RECEIVED_BACKGROUND_EVENT_NAME;
Expand Down Expand Up @@ -207,9 +209,14 @@ private void notifyReceivedBackgroundToJS() {

private void notifyOpenedToJS() {
Bundle response = new Bundle();
response.putBundle("notification", mNotificationProps.asBundle());

mJsIOHelper.sendEventToJS(NOTIFICATION_OPENED_EVENT_NAME, response, mAppLifecycleFacade.getRunningReactContext());
try
{
response.putBundle("notification", mNotificationProps.asBundle());
mJsIOHelper.sendEventToJS(NOTIFICATION_OPENED_EVENT_NAME, response, mAppLifecycleFacade.getRunningReactContext());
} catch (NullPointerException e)
{
Log.e(LOGTAG, "notifyOpenedToJS: Null pointer exception");
}
Comment on lines +212 to +219
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It actually won't fix the issue but will only prevent the app from crashing: instead of receiving a callback on JS side it will only log the "null pointer exception"

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case @artyorsh I would argue this solution is sufficient, taking into account:

  • It has been fixed similarly in another place in the codebase, where asBundle() call can also throw null pointer exceptions/
    public void getInitialNotification(final Promise promise) {
    if(BuildConfig.DEBUG) Log.d(LOGTAG, "Native method invocation: getInitialNotification");
    Object result = null;
    try {
    final PushNotificationProps notification = InitialNotificationHolder.getInstance().get();
    if (notification == null) {
    return;
    }
    result = Arguments.fromBundle(notification.asBundle());
    InitialNotificationHolder.getInstance().clear();
    } catch (NullPointerException e) {
    Log.e(LOGTAG, "getInitialNotification: Null pointer exception");
    } finally {
    promise.resolve(result);
  • The "intended" behavior of the library is still very much intact, push notifications are received and open properly. Just on some specific platforms (android 12) in some cases, it seems that the bundle can be null, and it would be better to just "bail" when there is no notification to handle rather than crash the app altogether 😅

A "root" cause for that might be more related to #837 , but @DanielEliraz should know more about that?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely better to have it caught than having the app crashing - true. But will it mean that in the bailed cases there will be no callback on JS? I think another scenario which might be related to the issue is that this function is called twice for some reasons - first time with "good" arguments and the second time with "bad". The fix will have more sense then 🙂

Copy link

@cchaabane cchaabane Jul 12, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

after some testing, @artyorsh 's guess is right. the function is called twice, first with the null bundle, then with the correct data.

@DanielEliraz can we have your review please ?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DanielEliraz is this being reviewed?

}

protected void launchOrResumeApp() {
Expand Down
11 changes: 11 additions & 0 deletions lib/dist/DTO/Notification.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export declare class Notification {
identifier: string;
payload: any;
constructor(payload: object);
get title(): string;
get body(): string;
get sound(): string;
get badge(): number;
get type(): string;
get thread(): string;
}
28 changes: 28 additions & 0 deletions lib/dist/DTO/Notification.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Notification = void 0;
class Notification {
constructor(payload) {
this.payload = payload;
this.identifier = this.payload.identifier;
}
get title() {
return this.payload.title;
}
get body() {
return this.payload.body;
}
get sound() {
return this.payload.sound;
}
get badge() {
return this.payload.badge;
}
get type() {
return this.payload.type;
}
get thread() {
return this.payload.thread;
}
}
exports.Notification = Notification;
1 change: 1 addition & 0 deletions lib/dist/DTO/Notification.test.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {};
52 changes: 52 additions & 0 deletions lib/dist/DTO/Notification.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const Notification_1 = require("./Notification");
const NotificationIOS_1 = require("./NotificationIOS");
const NotificationAndroid_1 = require("./NotificationAndroid");
describe('Notification', () => {
it('Should create notification with payload', () => {
const payload = { p: 'p' };
const notification = new Notification_1.Notification(payload);
expect(notification.payload).toEqual(payload);
});
it('Should create iOS notification with identifier', () => {
const payload = { identifier: 'identifier' };
const notification = new NotificationIOS_1.NotificationIOS(payload);
expect(notification.identifier).toEqual(payload.identifier);
});
it('Should create Android notification with identifier', () => {
const payload = { 'google.message_id': 'identifier' };
const notification = new NotificationAndroid_1.NotificationAndroid(payload);
expect(notification.identifier).toEqual('identifier');
});
it('Should return title from payload', () => {
const payload = { title: 'title' };
const notification = new Notification_1.Notification(payload);
expect(notification.title).toEqual(payload.title);
});
it('Should return body from payload', () => {
const payload = { body: 'body' };
const notification = new Notification_1.Notification(payload);
expect(notification.body).toEqual(payload.body);
});
it('Should return sound from payload', () => {
const payload = { sound: 'sound.mp4' };
const notification = new Notification_1.Notification(payload);
expect(notification.sound).toEqual(payload.sound);
});
it('Should return badge from payload', () => {
const payload = { badge: 1 };
const notification = new Notification_1.Notification(payload);
expect(notification.badge).toEqual(payload.badge);
});
it('Should return type from payload', () => {
const payload = { type: 'type' };
const notification = new Notification_1.Notification(payload);
expect(notification.type).toEqual(payload.type);
});
it('Should return thread from payload', () => {
const payload = { thread: 'thread' };
const notification = new Notification_1.Notification(payload);
expect(notification.thread).toEqual(payload.thread);
});
});
7 changes: 7 additions & 0 deletions lib/dist/DTO/NotificationAndroid.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Notification } from './Notification';
export declare class NotificationAndroid extends Notification {
constructor(payload: object);
get title(): string;
get body(): string;
get sound(): string;
}
20 changes: 20 additions & 0 deletions lib/dist/DTO/NotificationAndroid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NotificationAndroid = void 0;
const Notification_1 = require("./Notification");
class NotificationAndroid extends Notification_1.Notification {
constructor(payload) {
super(payload);
this.identifier = this.payload["google.message_id"];
}
get title() {
return this.payload.title;
}
get body() {
return this.payload.body;
}
get sound() {
return this.payload.sound;
}
}
exports.NotificationAndroid = NotificationAndroid;
1 change: 1 addition & 0 deletions lib/dist/DTO/NotificationAndroid.test.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {};
25 changes: 25 additions & 0 deletions lib/dist/DTO/NotificationAndroid.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const NotificationAndroid_1 = require("./NotificationAndroid");
describe('Notification', () => {
it('Should create notification with payload', () => {
const payload = { p: 'p' };
const notification = new NotificationAndroid_1.NotificationAndroid(payload);
expect(notification.payload).toEqual(payload);
});
it('Should return title from payload', () => {
const payload = { title: 'title', body: 'body' };
const notification = new NotificationAndroid_1.NotificationAndroid(payload);
expect(notification.title).toEqual('title');
});
it('Should return body from payload', () => {
const payload = { title: 'title', body: 'body' };
const notification = new NotificationAndroid_1.NotificationAndroid(payload);
expect(notification.body).toEqual('body');
});
it('Should return sound from payload', () => {
const payload = { title: 'title', sound: 'sound.wav' };
const notification = new NotificationAndroid_1.NotificationAndroid(payload);
expect(notification.sound).toEqual('sound.wav');
});
});
4 changes: 4 additions & 0 deletions lib/dist/DTO/NotificationFactory.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { Notification } from './Notification';
export declare class NotificationFactory {
fromPayload(payload: any): Notification;
}
18 changes: 18 additions & 0 deletions lib/dist/DTO/NotificationFactory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NotificationFactory = void 0;
const Notification_1 = require("./Notification");
const NotificationIOS_1 = require("./NotificationIOS");
const NotificationAndroid_1 = require("./NotificationAndroid");
const react_native_1 = require("react-native");
class NotificationFactory {
fromPayload(payload) {
if (react_native_1.Platform.OS === 'ios') {
return payload.aps ? new NotificationIOS_1.NotificationIOS(payload) : new Notification_1.Notification(payload);
}
else {
return new NotificationAndroid_1.NotificationAndroid(payload);
}
}
}
exports.NotificationFactory = NotificationFactory;
12 changes: 12 additions & 0 deletions lib/dist/DTO/NotificationIOS.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Notification } from './Notification';
export declare class NotificationIOS extends Notification {
identifier: string;
constructor(payload: object);
get aps(): any;
get alert(): any;
get title(): string;
get body(): string;
get sound(): string;
get badge(): number;
get thread(): string;
}
40 changes: 40 additions & 0 deletions lib/dist/DTO/NotificationIOS.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NotificationIOS = void 0;
const Notification_1 = require("./Notification");
const _ = require("lodash");
class NotificationIOS extends Notification_1.Notification {
constructor(payload) {
super(payload);
this.identifier = this.payload.identifier;
}
get aps() {
return this.payload.aps || {};
}
get alert() {
if (_.isObject(this.aps.alert)) {
return this.aps.alert;
}
else if (_.isString(this.aps.alert)) {
return {
body: this.aps.alert
};
}
}
get title() {
return this.alert.title;
}
get body() {
return this.alert.body;
}
get sound() {
return this.aps.sound;
}
get badge() {
return this.aps.badge;
}
get thread() {
return this.aps.thread;
}
}
exports.NotificationIOS = NotificationIOS;
1 change: 1 addition & 0 deletions lib/dist/DTO/NotificationIOS.test.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {};
58 changes: 58 additions & 0 deletions lib/dist/DTO/NotificationIOS.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const NotificationIOS_1 = require("./NotificationIOS");
describe('Notification', () => {
it('Should create notification with payload', () => {
const payload = { p: 'p' };
const notification = new NotificationIOS_1.NotificationIOS(payload);
expect(notification.payload).toEqual(payload);
});
it('Should create notification with valid aps', () => {
const aps = { alert: {} };
const payload = { aps };
const notification = new NotificationIOS_1.NotificationIOS(payload);
expect(notification.aps).toEqual(aps);
});
it('Should create notification with empy aps', () => {
const payload = { aps: undefined };
const notification = new NotificationIOS_1.NotificationIOS(payload);
expect(notification.aps).toEqual({});
});
it('Should return alert object', () => {
const payload = { aps: { alert: { title: 'title' } } };
const notification = new NotificationIOS_1.NotificationIOS(payload);
expect(notification.alert).toEqual(payload.aps.alert);
});
it('Should return alert object when alert is string', () => {
const payload = { aps: { alert: 'title' } };
const notification = new NotificationIOS_1.NotificationIOS(payload);
expect(notification.alert).toEqual({
body: 'title'
});
});
it('Should return title from alert', () => {
const payload = { aps: { alert: { title: 'title' } } };
const notification = new NotificationIOS_1.NotificationIOS(payload);
expect(notification.title).toEqual('title');
});
it('Should return body from alert', () => {
const payload = { aps: { alert: { title: 'title', body: 'body' } } };
const notification = new NotificationIOS_1.NotificationIOS(payload);
expect(notification.body).toEqual('body');
});
it('Should return badge from aps', () => {
const payload = { aps: { badge: 0 } };
const notification = new NotificationIOS_1.NotificationIOS(payload);
expect(notification.badge).toEqual(0);
});
it('Should return sound from aps', () => {
const payload = { aps: { sound: 'sound.wav' } };
const notification = new NotificationIOS_1.NotificationIOS(payload);
expect(notification.sound).toEqual('sound.wav');
});
it('Should return thread from aps', () => {
const payload = { aps: { thread: 'thread' } };
const notification = new NotificationIOS_1.NotificationIOS(payload);
expect(notification.thread).toEqual('thread');
});
});
61 changes: 61 additions & 0 deletions lib/dist/Notifications.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { EventsRegistry } from './events/EventsRegistry';
import { Notification } from './DTO/Notification';
import { NotificationCategory } from './interfaces/NotificationCategory';
import { NotificationChannel } from './interfaces/NotificationChannel';
import { NotificationsIOS } from './NotificationsIOS';
import { NotificationsAndroid } from './NotificationsAndroid';
import { NotificationPermissionOptions } from './interfaces/NotificationPermissions';
export declare class NotificationsRoot {
readonly _ios: NotificationsIOS;
readonly _android: NotificationsAndroid;
private readonly notificationFactory;
private readonly nativeEventsReceiver;
private readonly nativeCommandsSender;
private readonly commands;
private readonly eventsRegistry;
private readonly eventsRegistryIOS;
private readonly uniqueIdProvider;
private readonly completionCallbackWrapper;
constructor();
/**
* registerRemoteNotifications
*/
registerRemoteNotifications(options?: NotificationPermissionOptions): void;
/**
* postLocalNotification
*/
postLocalNotification(notification: Notification, id?: number): number;
/**
* getInitialNotification
*/
getInitialNotification(): Promise<Notification | undefined>;
/**
* setCategories
*/
setCategories(categories: [NotificationCategory?]): void;
/**
* cancelLocalNotification
*/
cancelLocalNotification(notificationId: number): void;
/**
* removeAllDeliveredNotifications
*/
removeAllDeliveredNotifications(): void;
/**
* isRegisteredForRemoteNotifications
*/
isRegisteredForRemoteNotifications(): Promise<boolean>;
/**
* setNotificationChannel
*/
setNotificationChannel(notificationChannel: NotificationChannel): void;
/**
* Obtain the events registry instance
*/
events(): EventsRegistry;
/**
* ios/android getters
*/
get ios(): NotificationsIOS;
get android(): NotificationsAndroid;
}
Loading