* Add typescript support

* Add unit tests

* Splits requestPermissionsWithCategories to two functions

* Done converting the js part to typescript

* typescript WIP

* Fix unit

* Fix e2e

* Identical API for Android and iOS

* Fix bundle

* build typescript before test-e2e-ios

* build typescript before test-e2e-ios

* Add docousaurus documentation

* Fix rebase from master

* Move android and ios folders into lib folder

* Split Notification.ts, Fix android example module

* Add test coverage for Notification.ts, clean old js files

* Updated docs

* Move ios and android commands to designated classes

* Remove package.json unused packages

* Fix e2e

* Fix docs, remove circleci config file

* 3.0.0-alpha.0

* Update README.md

* Fix js tests

* Add missing flavors

* Update release script

* Add pretest scripts

* Update release script

* Revert manual version change

* Fix release build

* Gradle resolve react-native version flavor

* Fix documentation website

* Add identical registerRemoteNotifications api for iOS and Android

* Finish API documentation

* Merge from master branch

* Fix build

* Remove NOTIFICATION_RECEIVED_FOREGROUND_EVENT_NAME

* Fix iOS example project

* Split specific iOS events, Update docs

* Add subscription documentation guide

* Add Local Notifications documentation guide

* Fix handling actions, Add event handling documentation guide

* Fix platforms logo

* Fix iOS unit tests

* Update package.json version to 3.0.0-beta.0 and generate CHANGELOG.gren.md [ci skip]

* Fix documentation

* Add prerelease script

* Update package.json version to 3.0.0-beta.1 and generate CHANGELOG.gren.md [ci skip]

* Add npm run docusaurus

* Add removeAllDeliveredNotifications support for both iOS and Android

* Add CI tag support

* Fix podspec

* Update iOS installation

* Fix android installation

* fix build.gradle rn package.json path

* Fix iOS

* Add NotificationFactory

* Fix tests

* Fix resolving gradle react native version

* find rn package.json by checking if the file exists instead of an exception in JsonSlurper

* Fix e2e

* Update package.json version to 3.0.0-beta.2 and generate CHANGELOG.gren.md [ci skip]

* Rename setBadgesCount to setBadgeCount

* add ios and android Notifications object getters

* Update package.json version to 3.0.0-beta.3 and generate CHANGELOG.gren.md [ci skip]

* Fix android token registration

* Update package.json version to 3.0.0-beta.4 and generate CHANGELOG.gren.md [ci skip]

Co-authored-by: wixmobile <41264282+wixmobile@users.noreply.github.com>
Co-authored-by: Artal Druk <artald@wix.com>
This commit is contained in:
Yogev Ben David
2020-01-15 17:13:07 +02:00
committed by GitHub
parent a6a5472d2b
commit 006e4ab86f
207 changed files with 3452 additions and 1648 deletions

View File

@@ -1,40 +0,0 @@
version: 2
jobs:
ios:
macos:
xcode: "10.2.1"
steps:
- checkout
- run:
name: Install Dependencies
command: scripts/install.ios.sh
- run:
name: npm install
command: npm install
- run:
name: iOS unit tests
command: 'npm run test-unit-ios'
- run:
name: Detox iOS e2e tests
command: 'npm run test-e2e-ios-release'
android:
macos:
xcode: "10.2.1"
steps:
- checkout
- run:
name: Install Android
command: scripts/install.android.sh
- run:
name: npm install
command: npm install
- run:
name: Android unit tests
command: 'npm run test-unit-android'
workflows:
version: 2
test:
jobs:
- ios
- android

2
.dockerignore Executable file
View File

@@ -0,0 +1,2 @@
*/node_modules
*.log

14
.gitignore vendored
View File

@@ -15,8 +15,12 @@ npm-debug.log
#####
# Android
android/local.properties
android/*.iml
lib/android/local.properties
lib/android/*.iml
lib/android/.idea
lib/android/build
lib/android/.gradle
*.gradle
#####
# OS X temporary files that should never be committed
@@ -187,6 +191,6 @@ coverage/
package-lock.json
.history
android/.idea
android/build
android/.gradle
# Typescript build
lib/dist/

View File

@@ -1,7 +1,11 @@
example/
test/
RNNotifications/DerivedData
node_modules/
website/
docs/
docs_old/
scripts/
e2e/
.eslintrc
*.yml
@@ -10,4 +14,5 @@ coverage/
android/.idea
android/build/
.idea
.history/
.history/
.github/

View File

@@ -1,5 +1,20 @@
# Changelog
## 3.0.0-beta.4 (14/01/2020)
*No changelog for this release.*
---
## 3.0.0-beta.3 (13/01/2020)
*No changelog for this release.*
---
## 3.0.0-beta.2 (13/01/2020)
*No changelog for this release.*
---
## 2.1.7 (14/12/2019)
*No changelog for this release.*

10
Dockerfile Executable file
View File

@@ -0,0 +1,10 @@
FROM node:8.11.4
WORKDIR /app/website
EXPOSE 3000 35729
COPY ./docs /app/docs
COPY ./website /app/website
RUN yarn install
CMD ["yarn", "start"]

View File

@@ -1,4 +1,7 @@
# React Native Notifications [![CircleCI](https://circleci.com/gh/wix/react-native-notifications/tree/master.svg?style=svg)](https://circleci.com/gh/wix/react-native-notifications/tree/master)
# React Native Notifications
![npm](https://img.shields.io/npm/dw/react-native-notifications.svg)
[![Build Status](https://img.shields.io/jenkins/s/http/jenkins-oss.wixpress.com:8080/job/multi-react-native-notifications-master.svg)](https://jenkins-oss.wixpress.com/job/multi-react-native-notifications-master/)
[![npm (tag)](https://img.shields.io/npm/v/react-native-notifications/snapshot.svg)](https://github.com/wix/react-native-navigation/tree/master)
Handle all the aspects of push notifications for your app, including remote and local notifications, interactive notifications, silent notifications, and more.
@@ -27,14 +30,8 @@ _For information regarding proper integration with [react-native-navigation](htt
_Upcoming: local notifications, background-state Rx queue (iOS equivalent)_
# Table of Content
- [Installation and setup](./docs/installation.md) - Setting up the library in your app
- [Subscription](./docs/subscription.md) - Signing in to push notifications vendors (e.g. GCM)
- [Notification Events (notfications core)](./docs/notificationsEvents.md) - Handling push notification arrival, notification opening by users
- [Local notifications](./docs/localNotifications.md) - Manually triggering notifications (i.e. not via push)
- [Advanced iOS topics](./docs/advancedIos.md) - e.g. managed notifications, PushKit API, Notifications actions
- [Notifications layout control - Android (wiki page)](https://github.com/wix/react-native-notifications/wiki/Android:-Layout-Customization) - Learn how to fully customize your notifications layout on Android!
# Quick Links
- [Documentation](https://wix.github.io/react-native-notifications/)
# License
The MIT License.

View File

@@ -1,15 +0,0 @@
#import <React/RCTEventEmitter.h>
static NSString* const RNRegistered = @"remoteNotificationsRegistered";
static NSString* const RNRegistrationFailed = @"remoteNotificationsRegistrationFailed";
static NSString* const RNPushKitRegistered = @"pushKitRegistered";
static NSString* const RNNotificationReceivedForeground = @"notificationReceivedForeground";
static NSString* const RNNotificationOpened = @"notificationOpened";
static NSString* const RNPushKitNotificationReceived = @"pushKitNotificationReceived";
@interface RNEventEmitter : RCTEventEmitter <RCTBridgeModule>
+ (void)sendEvent:(NSString *)event body:(NSDictionary *)body;
@end

18
docker-compose.yml Executable file
View File

@@ -0,0 +1,18 @@
version: "3"
services:
docusaurus:
build: .
ports:
- 3000:3000
- 35729:35729
volumes:
- ./docs:/app/docs
- ./website/blog:/app/website/blog
- ./website/core:/app/website/core
- ./website/i18n:/app/website/i18n
- ./website/pages:/app/website/pages
- ./website/static:/app/website/static
- ./website/sidebars.json:/app/website/sidebars.json
- ./website/siteConfig.js:/app/website/siteConfig.js
working_dir: /app/website

165
docs/advanced-ios.md Normal file
View File

@@ -0,0 +1,165 @@
---
id: advanced-ios
title: iOS Advanced API
sidebar_label: iOS
---
## PushKit API
The PushKit framework provides the classes for your iOS apps to receive background pushes from remote servers. it has better support for background notifications compared to regular push notifications with `content-available: 1`. More info in [iOS PushKit documentation](https://developer.apple.com/library/ios/documentation/NetworkingInternet/Reference/PushKit_Framework/).
### Register to PushKit
[Prepare your app to receive VoIP push notifications](https://developer.apple.com/library/ios/documentation/Performance/Conceptual/EnergyGuide-iOS/OptimizeVoIP.html)
### Listen to PushKit notifications
On receiving PushKit notification, a `pushKitNotificationReceived` event will be fired with the notification payload.
```js
Notifications.ios.events().registerPushKitNotificationReceived((payload: object) => {
console.log(JSON.stringify(payload));
});
```
In your ReactNative code, add event handler for `pushKitRegistered` event and call to `registerPushKit()`:
```javascript
constructor() {
Notifications.ios.events().registerPushKitRegistered((event: RegisteredPushKit) => {
console.log("PushKit Token Received: " + event.pushKitToken);
});
Notifications.ios.events().registerPushKitNotificationReceived((payload: object) => {
console.log('PushKit notification Received: ' + JSON.stringify(payload));
});
Notifications.ios.registerPushKit();
}
```
> 1. Notice that PushKit device token and regular notifications device token are different, so you must handle two different tokens in the server side in order to support this feature.
> 2. PushKit will not request permissions from the user for push notifications.
---
## Interactive / Actionable Notifications
> This section provides description for iOS. For notifications customization on Android, refer to [our wiki](https://github.com/wix/react-native-notifications/wiki/Android-Customizations#customizing-notifications-layout).
Interactive notifications allow you to reply to a message right from the notification banner or take action right from the lock screen.
On the Lock screen and within Notification Center, you swipe from right to left
to reveal actions. Destructive actions, like trashing an email, are color-coded red. Relatively neutral actions, like dismissing an alert or declining an invitation, are color-coded gray.
For banners, you pull down to reveal actions as buttons. For popups, the actions are immediately visible — the buttons are right there.
You can find more info about interactive notifications [here](http://www.imore.com/interactive-notifications-ios-8-explained).
![Interactive Notifications](http://i.imgur.com/XrVzy9w.gif)
Notification **actions** allow the user to interact with a given notification.
Notification **categories** allow you to group multiple actions together, and to connect the actions with the push notification itself.
Follow the basic workflow of adding interactive notifications to your app:
1. Config the actions.
2. Group actions together into categories.
3. Register to push notifications with the configured categories.
4. Push a notification (or trigger a [local](#triggering-local-notifications) one) with the configured category name.
### Example
#### Config the Actions
We will config two actions: upvote and reply.
```javascript
import { Notifications, NotificationAction, NotificationCategory } from 'react-native-notifications';
let upvoteAction = new NotificationAction({
activationMode: "background",
title: String.fromCodePoint(0x1F44D),
identifier: "UPVOTE_ACTION",
textInput: {
buttonTitle: 'title',
placeholder: 'placeholder text'
}
});
let replyAction = new NotificationAction({
activationMode: "background",
title: "Reply",
authenticationRequired: true,
identifier: "REPLY_ACTION"
});
```
#### Config the Category
We will group `upvote` action and `reply` action into a single category: `EXAMPLE_CATEGORY `. If the notification contains `EXAMPLE_CATEGORY ` under `category` field, those actions will appear.
```javascript
let exampleCategory = new NotificationCategory({
identifier: "EXAMPLE_CATEGORY",
actions: [upvoteAction, replyAction]
});
```
#### Register to Push Notifications
Instead of basic registration like we've done before, we will register the device to push notifications with the category we've just created.
```javascript
Notifications.setCategories([exampleCategory]);
```
#### Push an Interactive Notification
Notification payload should look like this:
```javascript
{
aps: {
// ... (alert, sound, badge, etc)
category: "EXAMPLE_CATEGORY"
}
}
```
The [example app](https://github.com/wix/react-native-notifications/tree/master/example) contains this interactive notification example, you can follow there.
### `NotificationAction` Payload
- `title` - Action button title.
- `identifier` - Action identifier (must be unique).
- `activationMode` - Indicating whether the app should activate to the foreground or background.
- `foreground` (default) - Activate the app and put it in the foreground.
- `background` - Activate the app and put it in the background. If the app is already in the foreground, it remains in the foreground.
- `textInput` - `TextInput` payload, when supplied, the system will present text input in this action.
- `destructive` - A Boolean value indicating whether the action is destructive. When the value of this property is `true`, the system displays the corresponding button differently to indicate that the action is destructive.
- `authenticationRequired` - A Boolean value indicating whether the user must unlock the device before the action is performed.
### `NotificationCategory` Payload
- `identifier` - The name of the action group (must be unique).
- `actions` - An array of `NotificationAction` objects, which related to this category.
### `TextInput` Payload
- `buttonTitle` - Title of the `send` button.
- `placeholder` - Placeholder for the `textInput`.
#### Get and set application icon badges count (iOS only)
Get the current number:
```javascript
Notifications.ios.getBadgeCount((count) => console.log(count));
```
Set to specific number:
```javascript
Notifications.ios.setBadgeCount(2);
```
Clear badges icon:
```javascript
Notifications.ios.setBadgeCount(0);
```

12
docs/android-api.md Executable file
View File

@@ -0,0 +1,12 @@
---
id: android-api
title: Android Specific Commands
sidebar_label: Android specific
---
## refreshToken()
Request a new token for sending push notifications.
```js
Notifications.android.refreshToken();
```

55
docs/general-api.md Executable file
View File

@@ -0,0 +1,55 @@
---
id: general-api
title: General Commands
sidebar_label: General
---
## registerRemoteNotifications()
Requests remote notification permissions, prompting the user's dialog box on iOS and request a token on Android.
If the user accept the remote notifications permissions, `RemoteNotificationsRegistered` event will get called with the device token.
```js
Notifications.registerRemoteNotifications();
```
## getInitialNotification()
This method returns a promise. If the app was launched by a push notification, this promise resolves to an object of type [Notification](notification-object). Otherwise, it resolves to undefined.
```js
const notification: Notification = await Notifications.getInitialNotification();
```
## postLocalNotification(notification, id?)
Posts local notification to the device notification center.
```js
Notifications.postLocalNotification({
body: 'Local notificiation!',
title: 'Local Notification Title',
sound: 'chime.aiff',
category: 'SOME_CATEGORY',
link: 'localNotificationLink',
fireDate: new Date()
}, id);
```
## cancelLocalNotification(id)
Relevant for notifications sent with `fireDate`.
```js
Notifications.cancelLocalNotification(id);
```
## isRegisteredForRemoteNotifications()
Check if the app has permissions to send remote notifications.
```js
const hasPermissions: boolean = await Notifications.isRegisteredForRemoteNotifications();
```
## removeAllDeliveredNotifications()
Remove all delivered notifications from Notification Center
```js
Notifications.removeAllDeliveredNotifications();
```

47
docs/general-events.md Executable file
View File

@@ -0,0 +1,47 @@
---
id: general-events
title: General
sidebar_label: General
---
## registerRemoteNotificationsRegistered()
Fired when the user registers for remote notifications. The handler will be invoked with an event holding the hex string representing the `deviceToken`
```js
Notifications.events().registerRemoteNotificationsRegistered((event: Registered) => {
console.log(event.deviceToken);
});
```
## registerNotificationReceived()
Fired when a remote notification is received in foreground state. The handler will be invoked with an instance of [Notification](notification-object).
Should call completion function on iOS, will be ignored on Android.
```js
Notifications.events().registerNotificationReceived((notification: Notification, completion: (response: NotificationCompletion) => void) => {
console.log(JSON.stringify(notification.data));
// Calling completion on iOS with `alert: true` will present the native iOS inApp notification.
completion({alert: true, sound: true, badge: false});
});
```
## registerRemoteNotificationOpened()
Fired when a remote notification is opened from dead or background state. The handler will be invoked with an instance of [Notification](notification-object).
Should call completion function on iOS, will be ignored on Android.
```js
Notifications.events().registerRemoteNotificationOpened((notification: Notification, completion: () => void) => {
console.log(JSON.stringify(notification.data));
completion();
});
```
## registerRemoteNotificationsRegistrationFailed()
Fired when the user fails to register for remote notifications. Typically occurs when APNS is having issues, or the device is a simulator. The handler will be invoked with {localizedDescription: string, code: string, domain: string}.
```js
Notifications.events().registerRemoteNotificationsRegistrationFailed((event: RegistrationError) => {
console.log(event.code, event.localizedDescription, event.domain);
});
```

64
docs/installation-android.md Executable file
View File

@@ -0,0 +1,64 @@
---
id: installation-android
title: Android Installation
sidebar_label: Android Installation
---
Add the library to your application class (e.g. `MainApplication.java`):
```java
import com.wix.reactnativenotifications.RNNotificationsPackage;
...
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
// ...
// Add this line:
new RNNotificationsPackage(MainApplication.this)
);
```
### Receiving push notifications
> Note: This section is only necessary in case you wish to be able to **receive** push notifications in your React-Native app.
Push notifications on Android are managed and dispatched using [Google's FCM service](https://firebase.google.com/docs/cloud-messaging). The following installation steps are a TL;DR of [Google's FCM setup guide](https://firebase.google.com/docs/cloud-messaging/android/client). You can follow them to get FCM integrated quickly, but we recommend that you will in the very least have a peek at the guide's overview.
#### Step #1: Subscribe to Google's FCM
To set FCM in your app, you must first create a google-services.json file. If you have no existing API project yet, the easiest way to go about in creating one is using [this step-by-step installation process](https://firebase.google.com/docs/android/setup);
#### Step #2: Copy google-services.json
After creating google-services.json, copy it into your project's android/app folder.
#### Step #3: Add google-services package to Project/build.gradle
```gradle
buildscript {
...
dependencies {
...
classpath 'com.google.gms:google-services:4.0.0'
}
}
```
#### Step #4: Add firebase-core package and apply google-services plugin in Project/app/build.gradle
```gradle
dependencies {
...
implementation 'com.google.firebase:firebase-core:16.0.0'
}
apply plugin: 'com.google.gms.google-services'
```
#### Step #5: Link react-native-notifications in Project/android/settings.gradle
```gradle
include ':react-native-notifications'
project(':react-native-notifications').projectDir = new File(rootProject.projectDir, '../../../node_modules/react-native-notifications/lib/android/app')
```

88
docs/installation-ios.md Executable file
View File

@@ -0,0 +1,88 @@
---
id: installation-ios
title: iOS Installation
sidebar_label: iOS Installation
---
As with any React Native project, the first step is to add the project as an npm dependency.
Start by running this:
```
$ npm install react-native-notifications --save
```
### Installation with CocoaPods
Projects generated using newer versions of react-native use CocoaPods by default. In that case it's easier to install react-native-navigation using CocoaPods.
1. Add the following to `Podfile`:
```diff
platform :ios, '9.0'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
target 'YourApp' do
# Pods for YourApp
pod 'React', :path => '../node_modules/react-native/'
pod 'React-Core', :path => '../node_modules/react-native/React'
pod 'React-DevSupport', :path => '../node_modules/react-native/React'
pod 'React-fishhook', :path => '../node_modules/react-native/Libraries/fishhook'
pod 'React-RCTActionSheet', :path => '../node_modules/react-native/Libraries/ActionSheetIOS'
pod 'React-RCTAnimation', :path => '../node_modules/react-native/Libraries/NativeAnimation'
pod 'React-RCTBlob', :path => '../node_modules/react-native/Libraries/Blob'
pod 'React-RCTImage', :path => '../node_modules/react-native/Libraries/Image'
pod 'React-RCTLinking', :path => '../node_modules/react-native/Libraries/LinkingIOS'
pod 'React-RCTNetwork', :path => '../node_modules/react-native/Libraries/Network'
pod 'React-RCTSettings', :path => '../node_modules/react-native/Libraries/Settings'
pod 'React-RCTText', :path => '../node_modules/react-native/Libraries/Text'
pod 'React-RCTVibration', :path => '../node_modules/react-native/Libraries/Vibration'
pod 'React-RCTWebSocket', :path => '../node_modules/react-native/Libraries/WebSocket'
pod 'React-cxxreact', :path => '../node_modules/react-native/ReactCommon/cxxreact'
pod 'React-jsi', :path => '../node_modules/react-native/ReactCommon/jsi'
pod 'React-jsiexecutor', :path => '../node_modules/react-native/ReactCommon/jsiexecutor'
pod 'React-jsinspector', :path => '../node_modules/react-native/ReactCommon/jsinspector'
pod 'yoga', :path => '../node_modules/react-native/ReactCommon/yoga'
pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec'
pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec'
+ pod 'ReactNativeNotifications', :podspec => '../node_modules/react-native-notifications/ReactNativeNotifications.podspec'
use_native_modules!
end
```
2. `cd ios && pod install`
3. Add the following line at the top of your `AppDelegate.m`
```objective-c
#import "RNNotifications.h"
```
Start monitor notifications in: `application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions`
```objective-c
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[RNNotifications startMonitorNotifications]; // -> Add this line
return YES;
}
```
And add the following methods to support registration:
```objective-c
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
[RNNotifications didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
[RNNotifications didFailToRegisterForRemoteNotificationsWithError:error];
}

70
docs/ios-api.md Executable file
View File

@@ -0,0 +1,70 @@
---
id: ios-api
title: iOS Specific Commands
sidebar_label: iOS specific
---
## requestPermissions()
Requests notification permissions from iOS, prompting the user's dialog box.
```js
Notifications.ios.requestPermissions();
```
## checkPermissions()
See what push permissions are currently enabled.
```js
Notifications.ios.checkPermissions();
```
## abandonPermissions()
Unregister for all remote notifications received via Apple Push Notification service.
You should call this method in rare circumstances only, such as when a new version of the app removes support for all types of remote notifications. Users can temporarily prevent apps from receiving remote notifications through the Notifications section of the Settings app. Apps unregistered through this method can always re-register.
```js
Notifications.ios.abandonPermissions();
```
## registerPushKit()
Register for PushKit notifications
```js
Notifications.ios.registerPushKit();
```
## cancelAllLocalNotifications()
Cancels all scheduled localNotifications
```js
Notifications.ios.cancelAllLocalNotifications();
```
## getDeliveredNotifications()
Provides you with a list of the apps notifications that are still displayed in Notification Center
```js
Notifications.ios.getDeliveredNotifications();
```
## removeDeliveredNotifications()
Removes the specified notifications from Notification Center
```js
Notifications.ios.removeDeliveredNotifications(identifiers);
```
## getBadgeCount()
Gets the badge count number from the aps object
```js
Notifications.ios.getBadgeCount();
```
## setBadgeCount()
Sets the badge number for the app icon on the home screen
```js
Notifications.ios.setBadgeCount(1);
```

24
docs/ios-events.md Executable file
View File

@@ -0,0 +1,24 @@
---
id: ios-events
title: iOS
sidebar_label: iOS specific
---
## registerPushKitRegistered()
Fired when the user registers for PushKit notifications. The handler will be invoked with an event holding the hex string representing the `pushKitToken`
```js
Notifications.ios.events().registerPushKitRegistered((event: RegisteredPushKit) => {
console.log(event.pushKitToken);
});
```
## registerPushKitNotificationReceived()
Fired when a PushKit notification is received. The handler will be invoked with the notification object.
```js
Notifications.ios.events().registerPushKitNotificationReceived((payload: object) => {
console.log(JSON.stringify(payload));
});
```

View File

@@ -1,7 +1,10 @@
---
id: localNotifications
title: Local Notifications
sidebar_label: Local Notifications
---
# Local Notifications
## <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Apple_logo_black.svg/2000px-Apple_logo_black.svg.png" width=30/> iOS
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Apple_logo_black.svg/2000px-Apple_logo_black.svg.png" width=30/> iOS
You can manually trigger local notifications in your JS code, to be posted immediately or in the future.
Triggering local notifications is fully compatible with React Native `PushNotificationsIOS` library.
@@ -9,7 +12,7 @@ Triggering local notifications is fully compatible with React Native `PushNotifi
Example:
```javascript
let localNotification = NotificationsIOS.localNotification({
let localNotification = Notifications.postLocalNotification({
body: "Local notificiation!",
title: "Local Notification Title",
sound: "chime.aiff",
@@ -32,12 +35,12 @@ Notification object contains:
### Cancel Scheduled Local Notifications
The `NotificationsIOS.localNotification()` and `NotificationsAndroid.localNotification()` methods return unique `notificationId` values, which can be used in order to cancel specific local notifications that were scheduled for delivery on `fireDate` and have not yet been delivered. You can cancel local notification by calling `NotificationsIOS.cancelLocalNotification(notificationId)` or `NotificationsAndroid.cancelLocalNotification(notificationId)`.
The `Notifications.postLocalNotification()` method return unique `notificationId` values, which can be used in order to cancel specific local notifications that were scheduled for delivery on `fireDate` and have not yet been delivered. You can cancel local notification by calling `Notifications.cancelLocalNotification(notificationId)`.
Example:
```javascript
let someLocalNotification = NotificationsIOS.localNotification({
let someLocalNotification = Notifications.postLocalNotification({
body: "Local notificiation!",
title: "Local Notification Title",
sound: "chime.aiff",
@@ -45,23 +48,23 @@ let someLocalNotification = NotificationsIOS.localNotification({
userInfo: { }
});
NotificationsIOS.cancelLocalNotification(someLocalNotification);
Notifications.cancelLocalNotification(someLocalNotification);
```
To cancel all local notifications (**iOS only!**), use `cancelAllLocalNotifications()`:
```javascript
NotificationsIOS.cancelAllLocalNotifications();
Notifications.ios.cancelAllLocalNotifications();
```
#### Cancel Delivered Local Notifications (iOS 10+ only)
To dismiss notifications from the notification center that have already been shown to the user, call `NotificationsIOS.removeDeliveredNotifications([notificationId])`:
To dismiss notifications from the notification center that have already been shown to the user, call `Notifications.ios.removeDeliveredNotifications([notificationId])`:
```javascript
let someLocalNotification = NotificationsIOS.localNotification({...});
let someLocalNotification = Notifications.postLocalNotification({...});
NotificationsIOS.removeDeliveredNotifications([someLocalNotification]);
Notifications.ios.removeDeliveredNotifications([someLocalNotification]);
```
Call `removeAllDeliveredNotifications()` to dismiss all delivered notifications
@@ -69,12 +72,12 @@ Call `removeAllDeliveredNotifications()` to dismiss all delivered notifications
notifications).
## <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/APK_format_icon.png/768px-APK_format_icon.png" width=30/> Android
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/APK_format_icon.png/768px-APK_format_icon.png" width=30/> Android
Much like on iOS, notifications can be triggered locally. The API to do so is a simplified version of the iOS equivalent that works more natually with the Android perception of push (remote) notifications:
```javascript
NotificationsAndroid.localNotification({
Notifications.postLocalNotification({
title: "Local notification",
body: "This notification was generated by the app!",
extra: "data"

25
docs/notification-object.md Executable file
View File

@@ -0,0 +1,25 @@
---
id: notification-object
title: Notification object
sidebar_label: Notification
---
Contains the payload data.
- **`title`**- returns the notification's title string.
- **`subtitle`**- returns the notification's title string. (iOS only)
- **`body`**- returns the notification's main message string.
- **`sound`**- returns the sound string from the `aps` object.
- **`badge`**- returns the badge count number from the `aps` object.
- **`category`**- returns the category from the `aps` object (related to interactive notifications).
- **`payload`**- returns the full payload sent from server.
Example:
```js
Notifications.events().registerNotificationReceived((notification: Notification, completion: (response: NotificationCompletion) => void) => {
// Prints the notification payload
console.log(JSON.stringify(notification.payload));
completion({alert: false, sound: false, badge: false});
});
```

View File

@@ -0,0 +1,56 @@
---
id: notifications-events
title: Handling Notification Events
sidebar_label: Events
---
When a push notification is received by the device, the application can be in one of the following states:
1. **Forground:** When the app is running and is used by the user right now; in this case, a `notificationReceived` event will be fired, do not forget to invoke `completion()` callback.
Finally, when a notification is _opened_ by the device user (i.e. tapped-on), a `notificationOpened` event is fired, here as well you need to remember invoking `completion()` callback.
Example:
```javascript
constructor() {
Notifications.events().registerNotificationReceived((notification: Notification, completion: (response: NotificationCompletion) => void) => {
console.log("Notification Received - Foreground", notification.data);
// Calling completion on iOS with `alert: true` will present the native iOS inApp notification.
completion({alert: true, sound: true, badge: false});
});
Notifications.events().registerRemoteNotificationOpened((notification: Notification, completion: () => void, action: NotificationActionResponse) => {
console.log("Notification opened by device user", notification.data);
console.log(`Notification opened with an action identifier: ${action.identifier} and response text: ${action.text}`);
completion();
});
}
```
### Notification Object
When you receive a push notification, you'll get an instance of [Notification](notification-object) object, contains the following methods:
## Querying initial notification
React-Native's [`PushNotificationsIOS.getInitialNotification()`](https://facebook.github.io/react-native/docs/pushnotificationios.html#getinitialnotification) allows for the async retrieval of the original notification used to open the App on iOS, but it has no equivalent implementation for Android.
```javascript
import {Notifications} from 'react-native-notifications';
Notifications.getInitialNotification()
.then((notification) => {
console.log("Initial notification was:", (notification ? notification.data : 'N/A'));
})
.catch((err) => console.error("getInitialNotifiation() failed", err));
```
> **Note**
>
> Notifications are considered 'initial' under the following terms:
> - User tapped on a notification, _AND_ -
> - App was either not running at all ("dead" state), _OR_ it existed in the background with **no running activities** associated with it.

View File

@@ -1,46 +1,32 @@
# Push Notifications Subscription
---
id: subscription
title: Push Notifications Subscription
sidebar_label: Subscription
---
The typical flow for subscribing a device for receiving push notification in real time is to first register the device at the vendor's servers (e.g. GCM), then publishing the received token to your own push management servers.
The typical flow for subscribing a device for receiving push notification in real time is to first register the device at the vendor's servers (e.g. FCM), then publishing the received token to your own push management servers.
This section is about the first part of the flow.
## <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Apple_logo_black.svg/2000px-Apple_logo_black.svg.png" width=30/> iOS
In order to handle notifications, you must register the `remoteNotificationsRegistered` event beforehand.
In your React Native app:
```javascript
import NotificationsIOS from 'react-native-notifications';
import {Notifications} from 'react-native-notifications';
class App extends Component {
constructor() {
NotificationsIOS.addEventListener('remoteNotificationsRegistered', this.onPushRegistered.bind(this));
NotificationsIOS.addEventListener('remoteNotificationsRegistrationFailed', this.onPushRegistrationFailed.bind(this));
NotificationsIOS.requestPermissions();
}
onPushRegistered(deviceToken) {
// TODO: Send the token to my server so it could send back push notifications...
console.log("Device Token Received", deviceToken);
}
Notifications.events().registerRemoteNotificationsRegistered((event: Registered) => {
// TODO: Send the token to my server so it could send back push notifications...
console.log("Device Token Received", event.deviceToken);
});
Notifications.events().registerRemoteNotificationsRegistrationFailed((event: RegistrationError) => {
console.error(event);
});
onPushRegistrationFailed(error) {
// For example:
//
// error={
// domain: 'NSCocoaErroDomain',
// code: 3010,
// localizedDescription: 'remote notifications are not supported in the simulator'
// }
console.error(error);
}
componentWillUnmount() {
// prevent memory leaks!
NotificationsIOS.removeEventListener('remoteNotificationsRegistered', this.onPushRegistered.bind(this));
NotificationsIOS.removeEventListener('remoteNotificationsRegistrationFailed', this.onPushRegistrationFailed.bind(this));
Notifications.requestPermissions();
}
}
@@ -48,30 +34,12 @@ class App extends Component {
When you have the device token, POST it to your server and register the device in your notifications provider (Amazon SNS, Azure, etc.).
You can check if the user granted permissions by calling `checkPermissions()`:
You can check if the user granted permissions on iOS by calling `checkPermissions()`:
```javascript
NotificationsIOS.checkPermissions().then((currentPermissions) => {
Notifications.ios.checkPermissions().then((currentPermissions) => {
console.log('Badges enabled: ' + !!currentPermissions.badge);
console.log('Sounds enabled: ' + !!currentPermissions.sound);
console.log('Alerts enabled: ' + !!currentPermissions.alert);
});
```
## <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/APK_format_icon.png/768px-APK_format_icon.png" width=30/> Android
Android works similarly but using a different API; The equivalent code is:
```javascript
import {NotificationsAndroid} from 'react-native-notifications';
// On Android, we allow for only one (global) listener per each event type.
NotificationsAndroid.setRegistrationTokenUpdateListener((deviceToken) => {
// TODO: Send the token to my server so it could send back push notifications...
console.log('Push-notifications registered!', deviceToken)
});
```
`deviceToken` being the token used to identify the device on the GCM.

View File

@@ -238,14 +238,14 @@ The [example app](https://github.com/wix/react-native-notifications/tree/master/
Get the current number:
```javascript
NotificationsIOS.getBadgesCount((count) => console.log(count));
NotificationsIOS.getBadgeCount((count) => console.log(count));
```
Set to specific number:
```javascript
NotificationsIOS.setBadgesCount(2);
NotificationsIOS.setBadgeCount(2);
```
Clear badges icon:
```javascript
NotificationsIOS.setBadgesCount(0);
NotificationsIOS.setBadgeCount(0);
```

View File

@@ -54,7 +54,7 @@ Add a reference to the library's native code in your global `settings.gradle`:
```gradle
include ':reactnativenotifications'
project(':reactnativenotifications').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-notifications/android/app')
project(':reactnativenotifications').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-notifications/lib/android/app')
```
Declare the library as a dependency in your **app-project's** `build.gradle`:

View File

@@ -0,0 +1,84 @@
# Local Notifications
## <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Apple_logo_black.svg/2000px-Apple_logo_black.svg.png" width=30/> iOS
You can manually trigger local notifications in your JS code, to be posted immediately or in the future.
Triggering local notifications is fully compatible with React Native `PushNotificationsIOS` library.
Example:
```javascript
let localNotification = NotificationsIOS.localNotification({
body: "Local notificiation!",
title: "Local Notification Title",
sound: "chime.aiff",
silent: false,
category: "SOME_CATEGORY",
userInfo: { }
});
```
Notification object contains:
- **`fireDate`**- The date and time when the system should deliver the notification (optinal - default is immidiate dispatch).
- `body`- The message displayed in the notification alert.
- `title`- The title of the notification, displayed in the notifications center.
- `alertAction`- The "action" displayed beneath an actionable notification on the lockscreen (e.g. "Slide to **open**"). Note that Apple no longer shows this in iOS 10.
- `sound`- The sound played when the notification is fired (optional -- will play default sound if unspecified). This must be the filename of a sound included in the application bundle; the sound must be 30 seconds or less and should be encoded with linear PCM or IMA4.
- `silent`- Whether the notification sound should be suppressed (optional).
- `category`- The category of this notification, required for [interactive notifications](#interactive--actionable-notifications-ios-only) (optional).
- `userInfo`- An optional object containing additional notification data.
### Cancel Scheduled Local Notifications
The `NotificationsIOS.localNotification()` and `NotificationsAndroid.localNotification()` methods return unique `notificationId` values, which can be used in order to cancel specific local notifications that were scheduled for delivery on `fireDate` and have not yet been delivered. You can cancel local notification by calling `NotificationsIOS.cancelLocalNotification(notificationId)` or `NotificationsAndroid.cancelLocalNotification(notificationId)`.
Example:
```javascript
let someLocalNotification = NotificationsIOS.localNotification({
body: "Local notificiation!",
title: "Local Notification Title",
sound: "chime.aiff",
category: "SOME_CATEGORY",
userInfo: { }
});
NotificationsIOS.cancelLocalNotification(someLocalNotification);
```
To cancel all local notifications (**iOS only!**), use `cancelAllLocalNotifications()`:
```javascript
NotificationsIOS.cancelAllLocalNotifications();
```
#### Cancel Delivered Local Notifications (iOS 10+ only)
To dismiss notifications from the notification center that have already been shown to the user, call `NotificationsIOS.removeDeliveredNotifications([notificationId])`:
```javascript
let someLocalNotification = NotificationsIOS.localNotification({...});
NotificationsIOS.removeDeliveredNotifications([someLocalNotification]);
```
Call `removeAllDeliveredNotifications()` to dismiss all delivered notifications
(note that this will dismiss push notifications in addition to local
notifications).
## <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/APK_format_icon.png/768px-APK_format_icon.png" width=30/> Android
Much like on iOS, notifications can be triggered locally. The API to do so is a simplified version of the iOS equivalent that works more natually with the Android perception of push (remote) notifications:
```javascript
NotificationsAndroid.localNotification({
title: "Local notification",
body: "This notification was generated by the app!",
extra: "data"
});
```
Upon notification opening (tapping by the device user), all data fields will be delivered as-is).

77
docs_old/subscription.md Normal file
View File

@@ -0,0 +1,77 @@
# Push Notifications Subscription
The typical flow for subscribing a device for receiving push notification in real time is to first register the device at the vendor's servers (e.g. GCM), then publishing the received token to your own push management servers.
This section is about the first part of the flow.
## <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Apple_logo_black.svg/2000px-Apple_logo_black.svg.png" width=30/> iOS
In order to handle notifications, you must register the `remoteNotificationsRegistered` event beforehand.
In your React Native app:
```javascript
import NotificationsIOS from 'react-native-notifications';
class App extends Component {
constructor() {
NotificationsIOS.addEventListener('remoteNotificationsRegistered', this.onPushRegistered.bind(this));
NotificationsIOS.addEventListener('remoteNotificationsRegistrationFailed', this.onPushRegistrationFailed.bind(this));
NotificationsIOS.requestPermissions();
}
onPushRegistered(deviceToken) {
// TODO: Send the token to my server so it could send back push notifications...
console.log("Device Token Received", deviceToken);
}
onPushRegistrationFailed(error) {
// For example:
//
// error={
// domain: 'NSCocoaErroDomain',
// code: 3010,
// localizedDescription: 'remote notifications are not supported in the simulator'
// }
console.error(error);
}
componentWillUnmount() {
// prevent memory leaks!
NotificationsIOS.removeEventListener('remoteNotificationsRegistered', this.onPushRegistered.bind(this));
NotificationsIOS.removeEventListener('remoteNotificationsRegistrationFailed', this.onPushRegistrationFailed.bind(this));
}
}
```
When you have the device token, POST it to your server and register the device in your notifications provider (Amazon SNS, Azure, etc.).
You can check if the user granted permissions by calling `checkPermissions()`:
```javascript
NotificationsIOS.checkPermissions().then((currentPermissions) => {
console.log('Badges enabled: ' + !!currentPermissions.badge);
console.log('Sounds enabled: ' + !!currentPermissions.sound);
console.log('Alerts enabled: ' + !!currentPermissions.alert);
});
```
## <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/APK_format_icon.png/768px-APK_format_icon.png" width=30/> Android
Android works similarly but using a different API; The equivalent code is:
```javascript
import {NotificationsAndroid} from 'react-native-notifications';
// On Android, we allow for only one (global) listener per each event type.
NotificationsAndroid.setRegistrationTokenUpdateListener((deviceToken) => {
// TODO: Send the token to my server so it could send back push notifications...
console.log('Push-notifications registered!', deviceToken)
});
```
`deviceToken` being the token used to identify the device on the GCM.

View File

@@ -9,7 +9,7 @@ describe('Notifications', () => {
describe('Foreground', () => {
it('Should receive notification', async () => {
await device.sendUserNotification(createNotification({link: 'foreground/notification'}));
await expect(elementByLabel('foreground/notification')).toBeVisible();
await linkShouldBeVisible('foreground/notification');
});
it('Should open notification', async () => {
@@ -30,9 +30,13 @@ describe('Notifications', () => {
describe('Dead state', () => {
it('Should receive notification', async () => {
await device.launchApp({newInstance: true, userNotification: createNotification({link: 'deadState/notification'})});
await expect(elementByLabel('deadState/notification')).toBeVisible();
await linkShouldBeVisible('deadState/notification');
});
});
async function linkShouldBeVisible(link) {
return await expect(elementByLabel(`Extra Link Param: ${link}`)).toBeVisible();
}
});
function createNotification({link, showAlert}) {

View File

@@ -2,8 +2,8 @@ apply plugin: "com.android.application"
project.ext.react = [
root : "../../../",
entryFile: "index.android.js",
bundleAssetName: "index.android.bundle",
entryFile: "index.js",
bundleAssetName: "index.bundle",
bundleInAlpha: true,
bundleInBeta: true
]
@@ -13,10 +13,9 @@ apply from: "../../../node_modules/react-native/react.gradle"
android {
compileSdkVersion 28
buildToolsVersion "28.0.3"
defaultConfig {
applicationId "com.wix.reactnativenotifications.app"
minSdkVersion 19
minSdkVersion 16
targetSdkVersion 28
versionCode 1
versionName "1.0"
@@ -24,7 +23,6 @@ android {
ndk {
abiFilters "armeabi-v7a", "x86"
}
missingDimensionStrategy "RNNotifications.reactNativeVersion", "reactNative60"
}
buildTypes {
release {

View File

@@ -1,11 +1,12 @@
package com.wix.reactnativenotifications.app;
import android.os.Bundle;
import com.facebook.react.ReactActivity;
public class MainActivity extends ReactActivity {
@Override
protected String getMainComponentName() {
return "WixRNNotifications";
return "NotificationsExampleApp";
}
}

View File

@@ -30,7 +30,12 @@ public class MainApplication extends Application implements ReactApplication {
return Arrays.asList(
new MainReactPackage(),
new RNNotificationsPackage(MainApplication.this)
);
);
}
@Override
protected String getJSMainModuleName() {
return "index";
}
};

View File

@@ -1,4 +1,4 @@
include ':myapplication'
include ':react-native-notifications'
project(':react-native-notifications').projectDir = new File(rootProject.projectDir, '../../android/app')
project(':react-native-notifications').projectDir = new File(rootProject.projectDir, '../../lib/android/app')

View File

@@ -1,168 +0,0 @@
'use strict';
import React, {Component} from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
Button,
TouchableHighlight
} from 'react-native';
import {NotificationsAndroid, PendingNotifications} from 'react-native-notifications';
let mainScreen;
function onPushRegistered() {
if (mainScreen) {
mainScreen.onPushRegistered();
}
}
function onNotificationOpened(notification) {
if (mainScreen) {
mainScreen.onNotificationOpened(notification)
}
}
function onNotificationReceived(notification) {
if (mainScreen) {
mainScreen.onNotificationReceived(notification)
}
}
// It's highly recommended to keep listeners registration at global scope rather than at screen-scope seeing that
// component mount and unmount lifecycle tends to be asymmetric!
NotificationsAndroid.setRegistrationTokenUpdateListener(onPushRegistered);
NotificationsAndroid.setNotificationOpenedListener(onNotificationOpened);
NotificationsAndroid.setNotificationReceivedListener(onNotificationReceived);
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
},
titleText: {
fontSize: 24,
textAlign: 'center',
margin: 10,
},
bodyText: {
fontSize: 18,
textAlign: 'center',
margin: 10,
},
mainButtonText: {
fontSize: 25,
fontStyle: 'italic',
fontWeight: 'bold',
textAlign: 'center',
margin: 10,
},
plainButtonText: {
fontSize: 18,
fontStyle: 'italic',
textAlign: 'center',
margin: 10,
},
});
class MainComponent extends Component {
constructor(props) {
super(props);
this.onPostNotification = this.onPostNotification.bind(this);
this.onCancelNotification = this.onCancelNotification.bind(this);
this.state = {
elapsed: 0,
lastNotification: undefined
};
console.log('ReactScreen', 'ReactScreen');
mainScreen = this;
setInterval(this.onTick.bind(this), 1000);
}
componentDidMount() {
console.log('ReactScreen', 'componentDidMount');
PendingNotifications.getInitialNotification()
.then((notification) => {console.log("getInitialNotification:", notification); this.setState({initialNotification: (notification ? notification.getData() : undefined)});})
.catch((err) => console.error("getInitialNotifiation failed", err));
}
componentWillUnmount() {
console.log('ReactScreen', 'componentWillUnmount');
}
onTick() {
this.setState({elapsed: this.state.elapsed + 1});
}
onPostNotification() {
this.lastNotificationId = NotificationsAndroid.localNotification({title: "Local notification", body: "This notification was generated by the app!"});
}
onCancelNotification() {
if (this.lastNotificationId) {
NotificationsAndroid.cancelLocalNotification(this.lastNotificationId);
this.lastNotificationId = undefined;
}
}
render() {
return (
<View style={styles.container}>
<Text style={styles.titleText}>Wix React Native Notifications</Text>
<Text style={styles.bodyText}>{this.state.initialNotification ? 'Opened from notification' : ''}</Text>
<Text style={styles.bodyText}>Last notification: {this.state.lastNotification ? '\n'+this.state.lastNotification.body + ` (opened at ''${this.state.notificationRxTime})` : "N/A"}</Text>
<Text style={styles.bodyText}>Time elapsed: {this.state.elapsed}</Text>
<Text>{"\n\n"}</Text>
<TouchableHighlight onPress={() => this.onPostNotification()}>
<Text style={styles.mainButtonText}>Try Me!</Text>
</TouchableHighlight>
<TouchableHighlight onPress={() => this.onCancelNotification()}>
<Text style={styles.plainButtonText}>Undo last</Text>
</TouchableHighlight>
<TouchableHighlight onPress={() => this.onCheckPermissions()}>
<Text style={styles.plainButtonText}>Check permissions</Text>
</TouchableHighlight>
<Button title={'Send local notification'} onPress={this.sendLocalNotification} testID={'sendLocalNotification'}/>
</View>
)
}
async onCheckPermissions() {
const hasPermissions = await NotificationsAndroid.isRegisteredForRemoteNotifications();
if (hasPermissions) {
alert('Yay! You have permissions');
} else {
alert('Boo! You don\'t have permissions');
}
}
sendLocalNotification() {
NotificationsAndroid.localNotification({
title: "Local notification",
body: "This notification was generated by the app!",
extra: "data"
});
}
onPushRegistered() {
}
onNotificationOpened(notification) {
console.log("onNotificationOpened: ", notification);
this.setState({lastNotification: notification.getData(), notificationRxTime: this.state.elapsed});
}
onNotificationReceived(notification) {
console.log("onNotificationReceived: ", notification);
}
}
AppRegistry.registerComponent('WixRNNotifications', () => MainComponent);

View File

@@ -1,160 +0,0 @@
import {
AppRegistry,
StyleSheet,
View,
Text,
Button
} from 'react-native';
import React, {Component} from 'react';
import NotificationsIOS, { NotificationAction, NotificationCategory } from 'react-native-notifications';
let upvoteAction = new NotificationAction({
activationMode: 'background',
title: String.fromCodePoint(0x1F44D),
identifier: 'UPVOTE_ACTION'
});
let replyAction = new NotificationAction({
activationMode: 'background',
title: 'Reply',
authenticationRequired: true,
textInput: {
buttonTitle: 'Reply now',
placeholder: 'Insert message'
},
identifier: 'REPLY_ACTION'
});
class NotificationsExampleApp extends Component {
constructor() {
super();
this.state = {
notifications: []
};
NotificationsIOS.addEventListener('remoteNotificationsRegistered', this.onPushRegistered.bind(this));
NotificationsIOS.addEventListener('remoteNotificationsRegistrationFailed', this.onPushRegisteredFailed.bind(this));
NotificationsIOS.addEventListener('pushKitRegistered', this.onPushKitRegistered.bind(this));
NotificationsIOS.registerPushKit();
NotificationsIOS.addEventListener('notificationReceivedForeground', this.onNotificationReceivedForeground.bind(this));
NotificationsIOS.addEventListener('notificationOpened', this.onNotificationOpened.bind(this));
NotificationsIOS.addEventListener('pushKitNotificationReceived', this.onPushKitNotificationReceived.bind(this));
}
async componentDidMount() {
const initialNotification = await NotificationsIOS.getInitialNotification();
if (initialNotification) {
this.setState({notifications: [initialNotification.getData().link, ...this.state.notifications]});
}
}
onPushRegistered(deviceToken) {
console.log('Device Token Received: ' + deviceToken);
}
onPushRegisteredFailed(error) {
console.log('Remote notifiction registration failed: ' + error);
}
onPushKitRegistered(deviceToken) {
console.log('PushKit Token Received: ' + deviceToken);
}
onPushKitNotificationReceived(notification) {
console.log('PushKit notification Received: ' + JSON.stringify(notification));
}
onNotificationReceivedForeground(notification, completion) {
console.log('Notification Received Foreground with title: ' + JSON.stringify(notification));
this.setState({
notifications: [...this.state.notifications, notification.getData().link]
});
completion({alert: notification.getData().showAlert, sound: false, badge: false});
}
onNotificationOpened(notification, completion, action) {
console.log('Notification Opened: ' + JSON.stringify(notification) + JSON.stringify(action));
this.setState({
notifications: [...this.state.notifications, `Notification Clicked: ${notification.getData().link}`]
});
completion();
}
renderNotification(notification) {
return <Text>{`${notification}`}</Text>;
}
render() {
const notifications = this.state.notifications.map((notification, idx) =>
(
<View key={`notification_${idx}`}>
{this.renderNotification(notification)}
</View>
));
return (
<View style={styles.container}>
<Button title={'Request permissions'} onPress={this.requestPermissions} testID={'requestPermissions'}/>
<Button title={'Send local notification'} onPress={this.sendLocalNotification} testID={'sendLocalNotification'}/>
<Button title={'Remove all delivered notifications'} onPress={this.removeAllDeliveredNotifications}/>
{notifications}
</View>
);
}
requestPermissions() {
let cat = new NotificationCategory({
identifier: 'SOME_CATEGORY',
actions: [upvoteAction, replyAction]
});
NotificationsIOS.requestPermissions([cat]);
}
sendLocalNotification() {
NotificationsIOS.localNotification({
body: 'Local notificiation!',
title: 'Local Notification Title',
sound: 'chime.aiff',
category: 'SOME_CATEGORY',
userInfo: { link: 'localNotificationLink' },
});
}
removeAllDeliveredNotifications() {
NotificationsIOS.removeAllDeliveredNotifications();
}
componentWillUnmount() {
NotificationsIOS.removeEventListener('notificationReceivedForeground', this.onNotificationReceivedForeground.bind(this));
NotificationsIOS.removeEventListener('notificationOpened', this.onNotificationOpened.bind(this));
NotificationsIOS.removeEventListener('remoteNotificationsRegistered', this.onPushRegistered.bind(this));
NotificationsIOS.removeEventListener('pushKitRegistered', this.onPushKitRegistered.bind(this));
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
AppRegistry.registerComponent('NotificationsExampleApp', () => NotificationsExampleApp);

157
example/index.js Normal file
View File

@@ -0,0 +1,157 @@
import {
AppRegistry,
StyleSheet,
View,
Text,
Button
} from 'react-native';
import React, {Component} from 'react';
import {Notifications, NotificationAction, NotificationCategory} from 'react-native-notifications';
class NotificationsExampleApp extends Component {
constructor() {
super();
this.state = {
notifications: [],
openedNotifications: [],
};
this.registerNotificationEvents();
this.setCategories();
}
registerNotificationEvents() {
Notifications.events().registerNotificationReceived((notification, completion) => {
this.setState({
notifications: [...this.state.notifications, notification]
});
completion({alert: notification.payload.showAlert, sound: false, badge: false});
});
Notifications.events().registerRemoteNotificationOpened((notification, completion) => {
this.setState({
openedNotifications: [...this.state.openedNotifications, notification]
});
completion();
});
}
requestPermissions() {
Notifications.registerRemoteNotifications();
}
setCategories() {
const upvoteAction = new NotificationAction({
activationMode: 'background',
title: String.fromCodePoint(0x1F44D),
identifier: 'UPVOTE_ACTION'
});
const replyAction = new NotificationAction({
activationMode: 'background',
title: 'Reply',
authenticationRequired: true,
textInput: {
buttonTitle: 'Reply now',
placeholder: 'Insert message'
},
identifier: 'REPLY_ACTION'
});
const category = new NotificationCategory({
identifier: 'SOME_CATEGORY',
actions: [upvoteAction, replyAction]
});
Notifications.setCategories([category]);
}
sendLocalNotification() {
Notifications.postLocalNotification({
body: 'Local notificiation!',
title: 'Local Notification Title',
sound: 'chime.aiff',
category: 'SOME_CATEGORY',
link: 'localNotificationLink',
});
}
removeAllDeliveredNotifications() {
Notifications.removeAllDeliveredNotifications();
}
async componentDidMount() {
const initialNotification = await Notifications.getInitialNotification();
if (initialNotification) {
this.setState({notifications: [initialNotification, ...this.state.notifications]});
}
}
renderNotification(notification) {
return (
<View style={{backgroundColor: 'lightgray', margin: 10}}>
<Text>{`Title: ${notification.title}`}</Text>
<Text>{`Body: ${notification.body}`}</Text>
<Text>{`Extra Link Param: ${notification.payload.link}`}</Text>
</View>
);
}
renderOpenedNotification(notification) {
return (
<View style={{backgroundColor: 'lightgray', margin: 10}}>
<Text>{`Title: ${notification.title}`}</Text>
<Text>{`Body: ${notification.body}`}</Text>
<Text>{`Notification Clicked: ${notification.payload.link}`}</Text>
</View>
);
}
render() {
const notifications = this.state.notifications.map((notification, idx) =>
(
<View key={`notification_${idx}`}>
{this.renderNotification(notification)}
</View>
));
const openedNotifications = this.state.openedNotifications.map((notification, idx) =>
(
<View key={`notification_${idx}`}>
{this.renderOpenedNotification(notification)}
</View>
));
return (
<View style={styles.container}>
<Button title={'Request permissions'} onPress={this.requestPermissions} testID={'requestPermissions'} />
<Button title={'Send local notification'} onPress={this.sendLocalNotification} testID={'sendLocalNotification'} />
<Button title={'Remove all delivered notifications'} onPress={this.removeAllDeliveredNotifications} />
{notifications}
{openedNotifications}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
AppRegistry.registerComponent('NotificationsExampleApp', () => NotificationsExampleApp);

View File

@@ -276,7 +276,7 @@
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = "<group>"; };
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = "<group>"; };
D84861172267695100E9103D /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
D85498C21D97B31100DEEE06 /* RNNotifications.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RNNotifications.xcodeproj; path = ../../RNNotifications/RNNotifications.xcodeproj; sourceTree = "<group>"; };
D85498C21D97B31100DEEE06 /* RNNotifications.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RNNotifications.xcodeproj; path = ../../lib/ios/RNNotifications.xcodeproj; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -431,6 +431,7 @@
832341AE1AAA6A7D00B99B32 /* Libraries */ = {
isa = PBXGroup;
children = (
50CBD3A222F2556900142352 /* RCTAnimation.xcodeproj */,
50F1F0A022CE3B0600FD5829 /* RCTNetwork.xcodeproj */,
D85498C21D97B31100DEEE06 /* RNNotifications.xcodeproj */,
146833FF1AC3E56700842450 /* React.xcodeproj */,
@@ -531,6 +532,10 @@
ProductGroup = 50F1F08622CE3A9F00FD5829 /* Products */;
ProjectRef = 50F1F08522CE3A9F00FD5829 /* RCTActionSheet.xcodeproj */;
},
{
ProductGroup = 50CBD3A322F2556900142352 /* Products */;
ProjectRef = 50CBD3A222F2556900142352 /* RCTAnimation.xcodeproj */;
},
{
ProductGroup = 50F1F09022CE3ABE00FD5829 /* Products */;
ProjectRef = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */;
@@ -866,7 +871,7 @@
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../node_modules/react-native/React/**",
"$(SRCROOT)/../node_modules/react-native-notifications/RNNotifications/**",
"$(SRCROOT)/../node_modules/react-native-notifications/lib/ios/**",
);
INFOPLIST_FILE = NotificationsExampleApp/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
@@ -891,7 +896,7 @@
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../node_modules/react-native/React/**",
"$(SRCROOT)/../node_modules/react-native-notifications/RNNotifications/**",
"$(SRCROOT)/../node_modules/react-native-notifications/lib/ios/**",
);
INFOPLIST_FILE = NotificationsExampleApp/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;

View File

@@ -70,7 +70,7 @@
BlueprintIdentifier = "508CE7C722D12B2600357815"
BuildableName = "RNNotificationsTests.xctest"
BlueprintName = "RNNotificationsTests"
ReferencedContainer = "container:../../RNNotifications/RNNotifications.xcodeproj">
ReferencedContainer = "container:../../lib/ios/RNNotifications.xcodeproj">
</BuildableReference>
</CodeCoverageTargets>
<Testables>
@@ -81,7 +81,7 @@
BlueprintIdentifier = "508CE7C722D12B2600357815"
BuildableName = "RNNotificationsTests.xctest"
BlueprintName = "RNNotificationsTests"
ReferencedContainer = "container:../../RNNotifications/RNNotifications.xcodeproj">
ReferencedContainer = "container:../../lib/ios/RNNotifications.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>

View File

@@ -1 +0,0 @@
require('./example/index');

View File

@@ -1,15 +1,44 @@
import groovy.json.JsonSlurper
apply plugin: 'com.android.library'
Object findReactNativePackageJson() {
def searchPath = 'node_modules/react-native/package.json'
def projectDir = project.projectDir.toString() + '/'
def rnPackageJsonFile = new File(projectDir + searchPath)
while (!rnPackageJsonFile.exists()) {
searchPath = '../' + searchPath
rnPackageJsonFile = new File(projectDir + searchPath)
}
return rnPackageJsonFile
}
String resolveFlavor() {
def packageSlurper = new JsonSlurper()
def rnPackageJsonFile = findReactNativePackageJson()
def reactNativePackageJson = packageSlurper.parseText(rnPackageJsonFile.text)
def reactNativeVersion = reactNativePackageJson.version
List versionComponents = reactNativeVersion.tokenize('.')
if (versionComponents[1].toInteger() < 60) {
return "reactNative59"
} else {
return "reactNative60"
}
}
android {
compileSdkVersion 28
buildToolsVersion '28.0.3'
defaultConfig {
minSdkVersion 19
minSdkVersion 16
targetSdkVersion 27
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
@@ -19,6 +48,7 @@ android {
debuggable true
}
}
testOptions {
unitTests.all { t ->
reports {
@@ -52,6 +82,14 @@ android {
dimension "RNNotifications.reactNativeVersion"
}
}
def flavor = resolveFlavor()
variantFilter { variant ->
def names = variant.flavors*.name
if (!names.contains(flavor)) {
setIgnore(true)
}
}
}
dependencies {

View File

@@ -6,6 +6,5 @@ public interface Defs {
String TOKEN_RECEIVED_EVENT_NAME = "remoteNotificationsRegistered";
String NOTIFICATION_RECEIVED_EVENT_NAME = "notificationReceived";
String NOTIFICATION_RECEIVED_FOREGROUND_EVENT_NAME = "notificationReceivedInForeground";
String NOTIFICATION_OPENED_EVENT_NAME = "notificationOpened";
}

View File

@@ -13,6 +13,7 @@ import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.wix.reactnativenotifications.core.AppLifecycleFacadeHolder;
import com.wix.reactnativenotifications.core.InitialNotificationHolder;
@@ -40,7 +41,7 @@ public class RNNotificationsModule extends ReactContextBaseJavaModule implements
@Override
public String getName() {
return "WixRNNotifications";
return "RNBridgeModule";
}
@Override
@@ -106,6 +107,10 @@ public class RNNotificationsModule extends ReactContextBaseJavaModule implements
}
@ReactMethod
public void setCategories(ReadableArray categories) {
}
public void cancelDeliveredNotification(String tag, int notificationId) {
IPushNotificationsDrawer notificationsDrawer = PushNotificationsDrawer.get(getReactApplicationContext().getApplicationContext());
notificationsDrawer.onNotificationClearRequest(tag, notificationId);

View File

@@ -23,7 +23,6 @@ import com.wix.reactnativenotifications.core.ProxyService;
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_FOREGROUND_EVENT_NAME;
public class PushNotification implements IPushNotification {
@@ -64,9 +63,6 @@ public class PushNotification implements IPushNotification {
public void onReceived() throws InvalidNotificationException {
postNotification(null);
notifyReceivedToJS();
if (mAppLifecycleFacade.isAppVisible()) {
notifiyReceivedForegroundNotificationToJS();
}
}
@Override
@@ -199,10 +195,6 @@ public class PushNotification implements IPushNotification {
mJsIOHelper.sendEventToJS(NOTIFICATION_RECEIVED_EVENT_NAME, mNotificationProps.asBundle(), mAppLifecycleFacade.getRunningReactContext());
}
private void notifiyReceivedForegroundNotificationToJS() {
mJsIOHelper.sendEventToJS(NOTIFICATION_RECEIVED_FOREGROUND_EVENT_NAME, mNotificationProps.asBundle(), mAppLifecycleFacade.getRunningReactContext());
}
private void notifyOpenedToJS() {
mJsIOHelper.sendEventToJS(NOTIFICATION_OPENED_EVENT_NAME, mNotificationProps.asBundle(), mAppLifecycleFacade.getRunningReactContext());
}

View File

@@ -6,16 +6,6 @@ public class PushNotificationProps {
protected Bundle mBundle;
public PushNotificationProps() {
mBundle = new Bundle();
}
public PushNotificationProps(String title, String body) {
mBundle = new Bundle();
mBundle.putString("title", title);
mBundle.putString("body", body);
}
public PushNotificationProps(Bundle bundle) {
mBundle = bundle;
}

View File

@@ -11,6 +11,8 @@ import com.google.android.gms.tasks.OnSuccessListener;
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.iid.InstanceIdResult;
import java.util.HashMap;
import static com.wix.reactnativenotifications.Defs.LOGTAG;
import static com.wix.reactnativenotifications.Defs.TOKEN_RECEIVED_EVENT_NAME;
@@ -86,6 +88,9 @@ public class FcmToken implements IFcmToken {
// Note: Cannot assume react-context exists cause this is an async dispatched service.
if (reactContext != null && reactContext.hasActiveCatalystInstance()) {
HashMap<String, String> h = new HashMap<String, String>() {{
put("deviceToken",sToken);
}};
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(TOKEN_RECEIVED_EVENT_NAME, sToken);
}
}

View File

@@ -0,0 +1,12 @@
package com.wix.reactnativenotifications;
import android.content.Context;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationManagerCompat;
public abstract class NotificationManagerCompatFacade {
public static NotificationManagerCompat from(@NonNull Context context) {
return NotificationManagerCompat.from(context);
}
}

View File

@@ -0,0 +1,12 @@
package com.wix.reactnativenotifications;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.core.app.NotificationManagerCompat;
public abstract class NotificationManagerCompatFacade {
public static NotificationManagerCompat from(@NonNull Context context) {
return NotificationManagerCompat.from(context);
}
}

View File

@@ -23,7 +23,7 @@ allprojects {
jcenter()
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url "$rootDir/../node_modules/react-native/android"
url "$rootDir/../../node_modules/react-native/android"
}
}
}

View File

@@ -10,7 +10,7 @@
@end
@interface RCTConvert (UNNotificationRequest)
+ (UNNotificationRequest *)UNNotificationRequest:(id)json withId:(NSString*)notificationId;
+ (UNNotificationRequest *)UNNotificationRequest:(id)json withId:(NSNumber*)notificationId;
@end
@interface RCTConvert (UNNotification)

View File

@@ -1,13 +1,5 @@
#import "RCTConvert+RNNotifications.h"
@implementation RCTConvert (UIUserNotificationActivationMode)
RCT_ENUM_CONVERTER(UIUserNotificationActivationMode, (@{
@"foreground": @(UIUserNotificationActivationModeForeground),
@"background": @(UIUserNotificationActivationModeBackground)
}), UIUserNotificationActivationModeForeground, integerValue)
@end
@implementation RCTConvert (UNNotificationActionOptions)
+ (UNNotificationActionOptions)UNUserNotificationActionOptions:(id)json {
@@ -63,7 +55,7 @@ RCT_ENUM_CONVERTER(UIUserNotificationActivationMode, (@{
@implementation RCTConvert (UNNotificationRequest)
+ (UNNotificationRequest *)UNNotificationRequest:(id)json withId:(NSString*)notificationId
+ (UNNotificationRequest *)UNNotificationRequest:(id)json withId:(NSNumber*)notificationId
{
NSDictionary<NSString *, id> *details = [self NSDictionary:json];
@@ -76,7 +68,7 @@ RCT_ENUM_CONVERTER(UIUserNotificationActivationMode, (@{
if ([RCTConvert BOOL:details[@"silent"]]) {
content.sound = nil;
}
content.userInfo = [RCTConvert NSDictionary:details[@"userInfo"]] ?: @{};
content.userInfo = [RCTConvert NSDictionary:details] ?: @{};
content.categoryIdentifier = [RCTConvert NSString:details[@"category"]];
NSDate *triggerDate = [RCTConvert NSDate:details[@"fireDate"]];
@@ -92,7 +84,7 @@ RCT_ENUM_CONVERTER(UIUserNotificationActivationMode, (@{
repeats:NO];
}
return [UNNotificationRequest requestWithIdentifier:notificationId
return [UNNotificationRequest requestWithIdentifier:[NSString stringWithFormat:@"%@", notificationId]
content:content trigger:trigger];
}
@@ -117,7 +109,8 @@ RCT_ENUM_CONVERTER(UIUserNotificationActivationMode, (@{
formattedNotification[@"body"] = RCTNullIfNil(content.body);
formattedNotification[@"category"] = RCTNullIfNil(content.categoryIdentifier);
formattedNotification[@"thread"] = RCTNullIfNil(content.threadIdentifier);
[formattedNotification addEntriesFromDictionary:RCTNullIfNil(RCTJSONClean(content.userInfo))];
[formattedNotification addEntriesFromDictionary:[NSDictionary dictionaryWithDictionary:RCTNullIfNil(RCTJSONClean(content.userInfo))]];
return formattedNotification;
}

View File

@@ -32,8 +32,12 @@ RCT_EXPORT_MODULE();
#pragma mark - JS interface
RCT_EXPORT_METHOD(requestPermissionsWithCategories:(NSArray *)json) {
[_commandsHandler requestPermissionsWithCategories:json];
RCT_EXPORT_METHOD(requestPermissions) {
[_commandsHandler requestPermissions];
}
RCT_EXPORT_METHOD(setCategories:(NSArray *)categories) {
[_commandsHandler setCategories:categories];
}
RCT_EXPORT_METHOD(getInitialNotification:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
@@ -56,16 +60,16 @@ RCT_EXPORT_METHOD(registerPushKit) {
[_commandsHandler registerPushKit];
}
RCT_EXPORT_METHOD(getBadgesCount:(RCTResponseSenderBlock)callback) {
[_commandsHandler getBadgesCount:callback];
RCT_EXPORT_METHOD(getBadgeCount:(RCTResponseSenderBlock)callback) {
[_commandsHandler getBadgeCount:callback];
}
RCT_EXPORT_METHOD(setBadgesCount:(int)count) {
[_commandsHandler setBadgesCount:count];
RCT_EXPORT_METHOD(setBadgeCount:(int)count) {
[_commandsHandler setBadgeCount:count];
}
RCT_EXPORT_METHOD(localNotification:(NSDictionary *)notification withId:(NSString *)notificationId) {
[_commandsHandler sendLocalNotification:notification withId:notificationId];
RCT_EXPORT_METHOD(postLocalNotification:(NSDictionary *)notification withId:(nonnull NSNumber *)notificationId) {
[_commandsHandler postLocalNotification:notification withId:notificationId];
}
RCT_EXPORT_METHOD(cancelLocalNotification:(NSString *)notificationId) {

Some files were not shown because too many files have changed in this diff Show More