diff --git a/.dockerignore b/.dockerignore new file mode 100755 index 0000000000000000000000000000000000000000..27d2dae2b493488b48bdb18b95af471821ece9bf --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +*/node_modules +*.log diff --git a/.eslintrc b/.eslintrc index cfb22942b448eed15ffd00d3cf0c114eadea86f0..fd6debbbe6a11c6cd3a9a3eec705f77c35543b42 100644 --- a/.eslintrc +++ b/.eslintrc @@ -2,7 +2,7 @@ "parser": "babel-eslint", "env": { "node": true, - "mocha": true, + "jest": true, "es6": true }, "extends": "eslint:recommended", @@ -18,7 +18,7 @@ "no-trailing-spaces": "error", "quotes": [ "error", - "double" + "single" ], "semi": [ "error", diff --git a/.github/main.workflow b/.github/main.workflow new file mode 100644 index 0000000000000000000000000000000000000000..24ebfb24e282626024ada778c0a590c5a6e274e6 --- /dev/null +++ b/.github/main.workflow @@ -0,0 +1,12 @@ +workflow "Verify labels" { + on = "pull_request" + resolves = "Enforce PR label" +} + +action "Enforce PR label" { + uses = "yogevbd/enforce-label-action@1.0.0" + secrets = ["GITHUB_TOKEN"] + env = { + VALID_LABELS = "bug,enhancement,feature,skip-changelog" + } +} \ No newline at end of file diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 0000000000000000000000000000000000000000..8f7fb1c0a16ee030d098f0d82c78ed0a4c058523 --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,52 @@ +# Configuration for probot-stale - https://github.com/probot/stale + +# Number of days of inactivity before an Issue or Pull Request becomes stale +daysUntilStale: 30 + +# Number of days of inactivity before a stale Issue or Pull Request is closed. +# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. +daysUntilClose: 7 + +# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable +exemptLabels: + - "type: accepted/enhancement" + - "user: looking for contributors" + - "📌 pinned" + +# Set to true to ignore issues in a project (defaults to false) +exemptProjects: false + +# Set to true to ignore issues in a milestone (defaults to false) +exemptMilestones: false + +# Label to use when marking as stale +staleLabel: "🏚 stale" + +# Comment to post when marking as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. + +# Comment to post when removing the stale label. +# unmarkComment: > +# Your comment here. + +# Comment to post when closing a stale Issue or Pull Request. +closeComment: > + The issue has been closed for inactivity. +# Limit the number of actions per hour, from 1-30. Default is 30 +limitPerRun: 30 + +# Limit to only `issues` or `pulls` +# only: issues + +# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls': +pulls: + daysUntilStale: 45 + markComment: > + This pull request has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. +# issues: +# exemptLabels: +# - confirmed diff --git a/.github/support.yml b/.github/support.yml new file mode 100644 index 0000000000000000000000000000000000000000..bb1140aa5f7a1ef6bc58980de1ca2da00c7f1f38 --- /dev/null +++ b/.github/support.yml @@ -0,0 +1,14 @@ +# Configuration for support-requests - https://github.com/dessant/support-requests + +# Label used to mark issues as support requests +supportLabel: "type: question/stack overflow" +# Comment to post on issues marked as support requests. Add a link +# to a support page, or set to `false` to disable +supportComment: > + We use the issue tracker exclusively for bug reports and feature requests. + This issue appears to be a general usage or support question. + Instead, please ask a question on Stack Overflow with the `react-native-notifications` tag. +# Whether to close issues marked as support requests +close: true +# Whether to lock issues marked as support requests +lock: false diff --git a/.gitignore b/.gitignore index a00cc16074dcbe47747f5bcac7f019307b6bf19a..82b4d3ace05a6a8f0ff3184ca261490023840e61 100644 --- a/.gitignore +++ b/.gitignore @@ -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 @@ -178,3 +182,15 @@ jspm_packages # Optional REPL history .node_repl_history + +# Intellij +*.iml + +coverage/ + +package-lock.json + +.history + +# Typescript build +lib/dist/ diff --git a/.grenrc.js b/.grenrc.js new file mode 100644 index 0000000000000000000000000000000000000000..8e8cf2555b17b9e52eb42b96ea86596c147c153f --- /dev/null +++ b/.grenrc.js @@ -0,0 +1,28 @@ +module.exports = { + template: { + commit: ({message, url, author, name}) => `- [${message}](${url}) - ${author ? `@${author}` : name}`, + issue: "- {{name}} [{{text}}]({{url}})", + label: "[**{{label}}**]", + noLabel: "closed", + group: "\n#### {{heading}}\n", + changelogTitle: "# Changelog\n\n", + release: "## {{release}} ({{date}})\n{{body}}", + releaseSeparator: "\n---\n\n" + }, + groupBy: { + "Enhancements:": ["enhancement", "internal"], + "Bug Fixes:": ["bug"], + "Features": ["feature"] + }, + ignoreIssuesWith: [ + "skip-changelog" + ], + ignoreTagsWith: [ + "snapshot" + ], + dataSource: "prs", + changelogFilename: "CHANGELOG.gren.md", + tags: "all", + override: true, + generate: true +} \ No newline at end of file diff --git a/.npmignore b/.npmignore index ab9335253ecbae009ded802179b840e2987180d0..62b5ab7b4a872ac29b635cf6237fa4266e490b99 100644 --- a/.npmignore +++ b/.npmignore @@ -1,7 +1,18 @@ example/ test/ -RNNotifications/DerivedData node_modules/ +website/ +docs/ +docs_old/ +scripts/ +e2e/ .eslintrc *.yml +coverage/ +.history +android/.idea +android/build/ +.idea +.history/ +.github/ \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 0fe294a6fbff7eacedb9883c44a1b8d49a734a20..6fb4c82d243c9d3f55efc591c427c37e187ea14d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,4 @@ language: node_js +osx_image: xcode10.1 node_js: - - "7" + - "10" diff --git a/CHANGELOG.gren.md b/CHANGELOG.gren.md new file mode 100644 index 0000000000000000000000000000000000000000..9d133f0e12d5b2645a4728ea8e7a35071206b03d --- /dev/null +++ b/CHANGELOG.gren.md @@ -0,0 +1,53 @@ +# Changelog + +## 3.0.0-beta.1 (07/10/2019) +*No changelog for this release.* + +--- + +## 3.0.0-beta.0 (02/10/2019) + +#### Bug Fixes: + +- Remove vibrate permissions maxSdkVersion, fix example project on android [#388](https://github.com/wix/react-native-notifications/pull/388) + +--- + +## 2.1.3 (22/09/2019) + +#### Enhancements: + +- Autogenerate changelog using github-release-notes [#382](https://github.com/wix/react-native-notifications/pull/382) + +#### Bug Fixes: + +- Fixing white square icon and annotation error [#379](https://github.com/wix/react-native-notifications/pull/379) + +--- + +## 3.0.0-alpha.0 (15/09/2019) +*No changelog for this release.* + +--- + +## 2.1.0 (14/09/2019) + +#### Enhancements: + +- React Native 0.60 Support [#375](https://github.com/wix/react-native-notifications/pull/375) +- New api changes ios [#342](https://github.com/wix/react-native-notifications/pull/342) + +--- + +## v1.4.0 (18/04/2019) +*No changelog for this release.* + +--- + +## v1.1.17 (26/10/2017) +*No changelog for this release.* + +--- + +## v1.1.12 (13/07/2017) +*No changelog for this release.* diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000000000000000000000000000000000..4865ffefe074252a570a978129450c6c76b36957 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,44 @@ +# Changelog +# 2.1.0 +## Added +* react-native 0.60 Support + +### Breaking Change +This version requires an additional installation step in order to identify the correct build flavor on android, as described in our [Installation doc](https://github.com/wix/react-native-notifications/blob/master/docs/installation.md#step-5-rnnotifications-and-react-native-version). + +# 2.0.6 +## Fixed +### Android +* Resolve intent by extra key and not by `google.message_id` string, Addresses #296. [#5056657](https://github.com/wix/react-native-notifications/pull/358/commits/5056657a6b3041b0c272357afcded42e59b83433) by [yogevbd](https://github.com/yogevbd) + +# 2.0.5 +## Fixed +### Android +* Reverted #349, This caused our e2e tests to fail, therefor we reverted this PR and will resolve this issue in the next version [#0b70828](https://github.com/wix/react-native-notifications/pull/357/commits/0b70828ca3e1f4e00817a32d6327381b4605c75c) by [yogevbd](https://github.com/yogevbd) + +# 2.0.4 +## Fixed +* Fix missing badge in silent notification payload with no aps.alert [#21c684d](https://github.com/wix/react-native-notifications/commit/21c684dbb7f632644747fa884c1b3f2bfd87f0a5) by [yogevbd](https://github.com/yogevbd) + +Moved our builds to CircleCI and added iOS unit and e2e tests coverage. + +# 2.0.3 + +## Fixed +### Android +* Remove verify notification [#57190f7](https://github.com/wix/react-native-notifications/commit/57190f7ed239022da28f62cb6e4d04e5fd5d48d1) by [yogevbd](https://github.com/yogevbd) +* Bring native unit tests back to life [#11f370b](https://github.com/wix/react-native-notifications/commit/11f370b380c4f9dd0365cc4866114722fa70a393) by [yogevbd](https://github.com/yogevbd) + +## Fixed +### iOS +* Fix iOS build on RN0.59 [#b90ea92](https://github.com/wix/react-native-notifications/commit/b90ea920b195a80bc218e15f58222af1701bf79f) by [lionerez1](https://github.com/lionerez1) + +### Breaking change +* Updated the android [installation setup guide](https://github.com/wix/react-native-notifications/blob/master/docs/installation.md). + +Make sure settings gradle imports from `'../node_modules/react-native-notifications/android/app'` and not `'../node_modules/react-native-notifications/android'` + +```gradle +include ':reactnativenotifications' +project(':reactnativenotifications').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-notifications/android/app') +``` diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000000000000000000000000000000000000..a5a52936f42f3da62691b30ddbf16580ed8ac6c2 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1 @@ +* @yogevbd diff --git a/Dockerfile b/Dockerfile new file mode 100755 index 0000000000000000000000000000000000000000..d369844d5bdf5b08e3462577e1e3da507c1573dd --- /dev/null +++ b/Dockerfile @@ -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"] diff --git a/README.md b/README.md index 173f7a61af8d01e4884eaba6373ef8ee671eecf9..8bfe2a68976ed4ab9d73f340a447c1568159a626 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ -# React Native Notifications [![Build Status](https://travis-ci.org/wix/react-native-notifications.svg)](https://travis-ci.org/wix/react-native-notifications) +# 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. diff --git a/RNNotifications/RNNotifications.h b/RNNotifications/RNNotifications.h deleted file mode 100644 index b1c34de28bfc3c481d70fff12c09491ed8f53975..0000000000000000000000000000000000000000 --- a/RNNotifications/RNNotifications.h +++ /dev/null @@ -1,19 +0,0 @@ -@import UIKit; - -#import -#import - -@interface RNNotifications : NSObject - -+ (void)didRegisterForRemoteNotificationsWithDeviceToken:(id)deviceToken; -+ (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error; -+ (void)didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings; -+ (void)didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSString *)type; - -+ (void)didReceiveRemoteNotification:(NSDictionary *)notification; -+ (void)didReceiveLocalNotification:(UILocalNotification *)notification; - -+ (void)handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler; -+ (void)handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler; - -@end diff --git a/RNNotifications/RNNotifications.m b/RNNotifications/RNNotifications.m deleted file mode 100644 index 844d8e242e007a210c13a60379dc671580f4b4d7..0000000000000000000000000000000000000000 --- a/RNNotifications/RNNotifications.m +++ /dev/null @@ -1,695 +0,0 @@ - -#import -#import -#import -#import -#import "RNNotifications.h" -#import -#import -#import "RNNotificationsBridgeQueue.h" -#import - -#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending) - -NSString* const RNNotificationCreateAction = @"CREATE"; -NSString* const RNNotificationClearAction = @"CLEAR"; - -NSString* const RNNotificationsRegistered = @"RNNotificationsRegistered"; -NSString* const RNNotificationsRegistrationFailed = @"RNNotificationsRegistrationFailed"; -NSString* const RNPushKitRegistered = @"RNPushKitRegistered"; -NSString* const RNNotificationReceivedForeground = @"RNNotificationReceivedForeground"; -NSString* const RNNotificationReceivedBackground = @"RNNotificationReceivedBackground"; -NSString* const RNNotificationOpened = @"RNNotificationOpened"; -NSString* const RNNotificationActionTriggered = @"RNNotificationActionTriggered"; - -/* - * Converters for Interactive Notifications - */ -@implementation RCTConvert (UIUserNotificationActivationMode) -RCT_ENUM_CONVERTER(UIUserNotificationActivationMode, (@{ - @"foreground": @(UIUserNotificationActivationModeForeground), - @"background": @(UIUserNotificationActivationModeBackground) - }), UIUserNotificationActivationModeForeground, integerValue) -@end - -@implementation RCTConvert (UIUserNotificationActionContext) -RCT_ENUM_CONVERTER(UIUserNotificationActionContext, (@{ - @"default": @(UIUserNotificationActionContextDefault), - @"minimal": @(UIUserNotificationActionContextMinimal) - }), UIUserNotificationActionContextDefault, integerValue) -@end - -@implementation RCTConvert (UIUserNotificationActionBehavior) -/* iOS 9 only */ -RCT_ENUM_CONVERTER(UIUserNotificationActionBehavior, (@{ - @"default": @(UIUserNotificationActionBehaviorDefault), - @"textInput": @(UIUserNotificationActionBehaviorTextInput) - }), UIUserNotificationActionBehaviorDefault, integerValue) -@end - -@implementation RCTConvert (UIMutableUserNotificationAction) -+ (UIMutableUserNotificationAction *)UIMutableUserNotificationAction:(id)json -{ - NSDictionary *details = [self NSDictionary:json]; - - UIMutableUserNotificationAction* action =[UIMutableUserNotificationAction new]; - action.activationMode = [RCTConvert UIUserNotificationActivationMode:details[@"activationMode"]]; - action.behavior = [RCTConvert UIUserNotificationActionBehavior:details[@"behavior"]]; - action.authenticationRequired = [RCTConvert BOOL:details[@"authenticationRequired"]]; - action.destructive = [RCTConvert BOOL:details[@"destructive"]]; - action.title = [RCTConvert NSString:details[@"title"]]; - action.identifier = [RCTConvert NSString:details[@"identifier"]]; - - return action; -} -@end - -@implementation RCTConvert (UIMutableUserNotificationCategory) -+ (UIMutableUserNotificationCategory *)UIMutableUserNotificationCategory:(id)json -{ - NSDictionary *details = [self NSDictionary:json]; - - UIMutableUserNotificationCategory* category = [UIMutableUserNotificationCategory new]; - category.identifier = details[@"identifier"]; - - // category actions - NSMutableArray* actions = [NSMutableArray new]; - for (NSDictionary* actionJson in [RCTConvert NSArray:details[@"actions"]]) { - [actions addObject:[RCTConvert UIMutableUserNotificationAction:actionJson]]; - } - - [category setActions:actions forContext:[RCTConvert UIUserNotificationActionContext:details[@"context"]]]; - - return category; -} -@end - -@implementation RCTConvert (UILocalNotification) -+ (UILocalNotification *)UILocalNotification:(id)json -{ - NSDictionary *details = [self NSDictionary:json]; - - UILocalNotification* notification = [UILocalNotification new]; - notification.fireDate = [RCTConvert NSDate:details[@"fireDate"]]; - notification.alertBody = [RCTConvert NSString:details[@"alertBody"]]; - notification.alertTitle = [RCTConvert NSString:details[@"alertTitle"]]; - notification.alertAction = [RCTConvert NSString:details[@"alertAction"]]; - notification.soundName = [RCTConvert NSString:details[@"soundName"]] ?: UILocalNotificationDefaultSoundName; - if ([RCTConvert BOOL:details[@"silent"]]) { - notification.soundName = nil; - } - notification.userInfo = [RCTConvert NSDictionary:details[@"userInfo"]] ?: @{}; - notification.category = [RCTConvert NSString:details[@"category"]]; - - return notification; -} -@end - -@implementation RCTConvert (UNNotificationRequest) -+ (UNNotificationRequest *)UNNotificationRequest:(id)json withId:(NSString*)notificationId -{ - NSDictionary *details = [self NSDictionary:json]; - - UNMutableNotificationContent *content = [UNMutableNotificationContent new]; - content.body = [RCTConvert NSString:details[@"alertBody"]]; - content.title = [RCTConvert NSString:details[@"alertTitle"]]; - content.sound = [RCTConvert NSString:details[@"soundName"]] - ? [UNNotificationSound soundNamed:[RCTConvert NSString:details[@"soundName"]]] - : [UNNotificationSound defaultSound]; - if ([RCTConvert BOOL:details[@"silent"]]) { - content.sound = nil; - } - content.userInfo = [RCTConvert NSDictionary:details[@"userInfo"]] ?: @{}; - content.categoryIdentifier = [RCTConvert NSString:details[@"category"]]; - - NSDate *triggerDate = [RCTConvert NSDate:details[@"fireDate"]]; - UNCalendarNotificationTrigger *trigger = nil; - if (triggerDate != nil) { - NSDateComponents *triggerDateComponents = [[NSCalendar currentCalendar] - components:NSCalendarUnitYear + - NSCalendarUnitMonth + NSCalendarUnitDay + - NSCalendarUnitHour + NSCalendarUnitMinute + - NSCalendarUnitSecond + NSCalendarUnitTimeZone - fromDate:triggerDate]; - trigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:triggerDateComponents - repeats:NO]; - } - - return [UNNotificationRequest requestWithIdentifier:notificationId - content:content trigger:trigger]; -} -@end - -static NSDictionary *RCTFormatUNNotification(UNNotification *notification) -{ - NSMutableDictionary *formattedNotification = [NSMutableDictionary dictionary]; - UNNotificationContent *content = notification.request.content; - - formattedNotification[@"identifier"] = notification.request.identifier; - - if (notification.date) { - NSDateFormatter *formatter = [NSDateFormatter new]; - [formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"]; - NSString *dateString = [formatter stringFromDate:notification.date]; - formattedNotification[@"fireDate"] = dateString; - } - - formattedNotification[@"alertTitle"] = RCTNullIfNil(content.title); - formattedNotification[@"alertBody"] = RCTNullIfNil(content.body); - formattedNotification[@"category"] = RCTNullIfNil(content.categoryIdentifier); - formattedNotification[@"thread-id"] = RCTNullIfNil(content.threadIdentifier); - formattedNotification[@"userInfo"] = RCTNullIfNil(RCTJSONClean(content.userInfo)); - - return formattedNotification; -} - -@implementation RNNotifications - -RCT_EXPORT_MODULE() - -@synthesize bridge = _bridge; - -- (void)dealloc -{ - [[NSNotificationCenter defaultCenter] removeObserver:self]; -} - -- (void)setBridge:(RCTBridge *)bridge -{ - _bridge = bridge; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(handleNotificationsRegistered:) - name:RNNotificationsRegistered - object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(handleNotificationsRegistrationFailed:) - name:RNNotificationsRegistrationFailed - object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(handlePushKitRegistered:) - name:RNPushKitRegistered - object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(handleNotificationReceivedForeground:) - name:RNNotificationReceivedForeground - object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(handleNotificationReceivedBackground:) - name:RNNotificationReceivedBackground - object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(handleNotificationOpened:) - name:RNNotificationOpened - object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(handleNotificationActionTriggered:) - name:RNNotificationActionTriggered - object:nil]; - - [RNNotificationsBridgeQueue sharedInstance].openedRemoteNotification = [_bridge.launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey]; - UILocalNotification *localNotification = [_bridge.launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey]; - [RNNotificationsBridgeQueue sharedInstance].openedLocalNotification = localNotification ? localNotification.userInfo : nil; -} - -/* - * Public Methods - */ -+ (void)didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings -{ - if ([UIApplication instancesRespondToSelector:@selector(registerForRemoteNotifications)]) { - [[UIApplication sharedApplication] registerForRemoteNotifications]; - } -} - -+ (void)didRegisterForRemoteNotificationsWithDeviceToken:(id)deviceToken -{ - NSString *tokenRepresentation = [deviceToken isKindOfClass:[NSString class]] ? deviceToken : [self deviceTokenToString:deviceToken]; - [[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationsRegistered - object:self - userInfo:@{@"deviceToken": tokenRepresentation}]; -} - -+ (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { - [[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationsRegistrationFailed - object:self - userInfo:@{@"code": [NSNumber numberWithInteger:error.code], @"domain": error.domain, @"localizedDescription": error.localizedDescription}]; -} - -+ (void)didReceiveRemoteNotification:(NSDictionary *)notification -{ - UIApplicationState state = [UIApplication sharedApplication].applicationState; - - if ([RNNotificationsBridgeQueue sharedInstance].jsIsReady == YES) { - // JS thread is ready, push the notification to the bridge - - if (state == UIApplicationStateActive) { - // Notification received foreground - [self didReceiveNotificationOnForegroundState:notification]; - } else if (state == UIApplicationStateInactive) { - // Notification opened - [self didNotificationOpen:notification]; - } else { - // Notification received background - [self didReceiveNotificationOnBackgroundState:notification]; - } - } else { - // JS thread is not ready - store it in the native notifications queue - [[RNNotificationsBridgeQueue sharedInstance] postNotification:notification]; - } -} - -+ (void)didReceiveLocalNotification:(UILocalNotification *)notification -{ - UIApplicationState state = [UIApplication sharedApplication].applicationState; - - NSMutableDictionary* newUserInfo = notification.userInfo.mutableCopy; - [newUserInfo removeObjectForKey:@"__id"]; - notification.userInfo = newUserInfo; - - if (state == UIApplicationStateActive) { - [self didReceiveNotificationOnForegroundState:notification.userInfo]; - } else if (state == UIApplicationStateInactive) { - NSString* notificationId = [notification.userInfo objectForKey:@"notificationId"]; - if (notificationId) { - [self clearNotificationFromNotificationsCenter:notificationId]; - } - [self didNotificationOpen:notification.userInfo]; - } -} - -+ (void)handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler -{ - [self emitNotificationActionForIdentifier:identifier responseInfo:responseInfo userInfo:notification.userInfo completionHandler:completionHandler]; -} - -+ (void)handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler -{ - [self emitNotificationActionForIdentifier:identifier responseInfo:responseInfo userInfo:userInfo completionHandler:completionHandler]; -} - -/* - * Notification handlers - */ -+ (void)didReceiveNotificationOnForegroundState:(NSDictionary *)notification -{ - [[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationReceivedForeground - object:self - userInfo:notification]; -} - -+ (void)didReceiveNotificationOnBackgroundState:(NSDictionary *)notification -{ - NSDictionary* managedAps = [notification objectForKey:@"managedAps"]; - NSDictionary* alert = [managedAps objectForKey:@"alert"]; - NSString* action = [managedAps objectForKey:@"action"]; - NSString* notificationId = [managedAps objectForKey:@"notificationId"]; - - if (action) { - // create or delete notification - if ([action isEqualToString: RNNotificationCreateAction] - && notificationId - && alert) { - [self dispatchLocalNotificationFromNotification:notification]; - - } else if ([action isEqualToString: RNNotificationClearAction] && notificationId) { - [self clearNotificationFromNotificationsCenter:notificationId]; - } - } - - [[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationReceivedBackground - object:self - userInfo:notification]; -} - -+ (void)didNotificationOpen:(NSDictionary *)notification -{ - [[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationOpened - object:self - userInfo:notification]; -} - -/* - * Helper methods - */ -+ (void)dispatchLocalNotificationFromNotification:(NSDictionary *)notification -{ - NSDictionary* managedAps = [notification objectForKey:@"managedAps"]; - NSDictionary* alert = [managedAps objectForKey:@"alert"]; - NSString* action = [managedAps objectForKey:@"action"]; - NSString* notificationId = [managedAps objectForKey:@"notificationId"]; - - if ([action isEqualToString: RNNotificationCreateAction] - && notificationId - && alert) { - - // trigger new client push notification - UILocalNotification* note = [UILocalNotification new]; - note.alertTitle = [alert objectForKey:@"title"]; - note.alertBody = [alert objectForKey:@"body"]; - note.userInfo = notification; - note.soundName = [managedAps objectForKey:@"sound"]; - note.category = [managedAps objectForKey:@"category"]; - - [[UIApplication sharedApplication] presentLocalNotificationNow:note]; - - // Serialize it and store so we can delete it later - NSData* data = [NSKeyedArchiver archivedDataWithRootObject:note]; - NSString* notificationKey = [self buildNotificationKeyfromNotification:notificationId]; - [[NSUserDefaults standardUserDefaults] setObject:data forKey:notificationKey]; - [[NSUserDefaults standardUserDefaults] synchronize]; - - NSLog(@"Local notification was triggered: %@", notificationKey); - } -} - -+ (void)clearNotificationFromNotificationsCenter:(NSString *)notificationId -{ - NSString* notificationKey = [self buildNotificationKeyfromNotification:notificationId]; - NSData* data = [[NSUserDefaults standardUserDefaults] objectForKey:notificationKey]; - if (data) { - UILocalNotification* notification = [NSKeyedUnarchiver unarchiveObjectWithData: data]; - - // delete the notification - [[UIApplication sharedApplication] cancelLocalNotification:notification]; - [[NSUserDefaults standardUserDefaults] removeObjectForKey:notificationKey]; - - NSLog(@"Local notification removed: %@", notificationKey); - - return; - } -} - -+ (NSString *)buildNotificationKeyfromNotification:(NSString *)notificationId -{ - return [NSString stringWithFormat:@"%@.%@", [[NSBundle mainBundle] bundleIdentifier], notificationId]; -} - -+ (NSString *)deviceTokenToString:(NSData *)deviceToken -{ - NSMutableString *result = [NSMutableString string]; - NSUInteger deviceTokenLength = deviceToken.length; - const unsigned char *bytes = deviceToken.bytes; - for (NSUInteger i = 0; i < deviceTokenLength; i++) { - [result appendFormat:@"%02x", bytes[i]]; - } - - return [result copy]; -} - -+ (void)requestPermissionsWithCategories:(NSMutableSet *)categories -{ - UIUserNotificationType types = (UIUserNotificationType) (UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert); - UIUserNotificationSettings* settings = [UIUserNotificationSettings settingsForTypes:types categories:categories]; - - [[UIApplication sharedApplication] registerUserNotificationSettings:settings]; -} - -+ (void)emitNotificationActionForIdentifier:(NSString *)identifier responseInfo:(NSDictionary *)responseInfo userInfo:(NSDictionary *)userInfo completionHandler:(void (^)())completionHandler -{ - NSString* completionKey = [NSString stringWithFormat:@"%@.%@", identifier, [NSString stringWithFormat:@"%d", (long)[[NSDate date] timeIntervalSince1970]]]; - NSMutableDictionary* info = [[NSMutableDictionary alloc] initWithDictionary:@{ @"identifier": identifier, @"completionKey": completionKey }]; - - // add text - NSString* text = [responseInfo objectForKey:UIUserNotificationActionResponseTypedTextKey]; - if (text != NULL) { - info[@"text"] = text; - } - - // add notification custom data - if (userInfo != NULL) { - info[@"notification"] = userInfo; - } - - // Emit event to the queue (in order to store the completion handler). if JS thread is ready, post it also to the notification center (to the bridge). - [[RNNotificationsBridgeQueue sharedInstance] postAction:info withCompletionKey:completionKey andCompletionHandler:completionHandler]; - - if ([RNNotificationsBridgeQueue sharedInstance].jsIsReady == YES) { - [[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationActionTriggered - object:self - userInfo:info]; - } -} - -+ (void)registerPushKit -{ - // Create a push registry object - PKPushRegistry* pushKitRegistry = [[PKPushRegistry alloc] initWithQueue:dispatch_get_main_queue()]; - - // Set the registry delegate to app delegate - pushKitRegistry.delegate = [[UIApplication sharedApplication] delegate]; - pushKitRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP]; -} - -+ (void)didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSString *)type -{ - [[NSNotificationCenter defaultCenter] postNotificationName:RNPushKitRegistered - object:self - userInfo:@{@"pushKitToken": [self deviceTokenToString:credentials.token]}]; -} - -- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type -{ - [RNNotifications didReceiveRemoteNotification:payload.dictionaryPayload]; -} - -/* - * Javascript events - */ -- (void)handleNotificationsRegistered:(NSNotification *)notification -{ - [_bridge.eventDispatcher sendDeviceEventWithName:@"remoteNotificationsRegistered" body:notification.userInfo]; -} - -- (void)handleNotificationsRegistrationFailed:(NSNotification *)notification -{ - [_bridge.eventDispatcher sendDeviceEventWithName:@"remoteNotificationsRegistrationFailed" body:notification.userInfo]; -} - -- (void)handlePushKitRegistered:(NSNotification *)notification -{ - [_bridge.eventDispatcher sendDeviceEventWithName:@"pushKitRegistered" body:notification.userInfo]; -} - -- (void)handleNotificationReceivedForeground:(NSNotification *)notification -{ - [_bridge.eventDispatcher sendDeviceEventWithName:@"notificationReceivedForeground" body:notification.userInfo]; -} - -- (void)handleNotificationReceivedBackground:(NSNotification *)notification -{ - [_bridge.eventDispatcher sendDeviceEventWithName:@"notificationReceivedBackground" body:notification.userInfo]; -} - -- (void)handleNotificationOpened:(NSNotification *)notification -{ - [_bridge.eventDispatcher sendDeviceEventWithName:@"notificationOpened" body:notification.userInfo]; -} - -- (void)handleNotificationActionTriggered:(NSNotification *)notification -{ - [_bridge.eventDispatcher sendAppEventWithName:@"notificationActionReceived" body:notification.userInfo]; -} - -/* - * React Native exported methods - */ -RCT_EXPORT_METHOD(requestPermissionsWithCategories:(NSArray *)json) -{ - NSMutableSet* categories = nil; - - if ([json count] > 0) { - categories = [NSMutableSet new]; - for (NSDictionary* categoryJson in json) { - [categories addObject:[RCTConvert UIMutableUserNotificationCategory:categoryJson]]; - } - } - - [RNNotifications requestPermissionsWithCategories:categories]; -} - -RCT_EXPORT_METHOD(getInitialNotification:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) -{ - NSDictionary * notification = nil; - notification = [RNNotificationsBridgeQueue sharedInstance].openedRemoteNotification ? - [RNNotificationsBridgeQueue sharedInstance].openedRemoteNotification : - [RNNotificationsBridgeQueue sharedInstance].openedLocalNotification; - [RNNotificationsBridgeQueue sharedInstance].openedRemoteNotification = nil; - [RNNotificationsBridgeQueue sharedInstance].openedLocalNotification = nil; - resolve(notification); -} - -RCT_EXPORT_METHOD(log:(NSString *)message) -{ - NSLog(message); -} - -RCT_EXPORT_METHOD(completionHandler:(NSString *)completionKey) -{ - [[RNNotificationsBridgeQueue sharedInstance] completeAction:completionKey]; -} - -RCT_EXPORT_METHOD(abandonPermissions) -{ - [[UIApplication sharedApplication] unregisterForRemoteNotifications]; -} - -RCT_EXPORT_METHOD(registerPushKit) -{ - [RNNotifications registerPushKit]; -} - -RCT_EXPORT_METHOD(getBadgesCount:(RCTResponseSenderBlock)callback) -{ - NSInteger count = [UIApplication sharedApplication].applicationIconBadgeNumber; - callback(@[ [NSNumber numberWithInteger:count] ]); -} - -RCT_EXPORT_METHOD(setBadgesCount:(int)count) -{ - [[UIApplication sharedApplication] setApplicationIconBadgeNumber:count]; -} - -RCT_EXPORT_METHOD(backgroundTimeRemaining:(RCTResponseSenderBlock)callback) -{ - NSTimeInterval remainingTime = [UIApplication sharedApplication].backgroundTimeRemaining; - callback(@[ [NSNumber numberWithDouble:remainingTime] ]); -} - -RCT_EXPORT_METHOD(consumeBackgroundQueue) -{ - // Mark JS Thread as ready - [RNNotificationsBridgeQueue sharedInstance].jsIsReady = YES; - - // Push actions to JS - [[RNNotificationsBridgeQueue sharedInstance] consumeActionsQueue:^(NSDictionary* action) { - [[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationActionTriggered - object:self - userInfo:action]; - }]; - - // Push background notifications to JS - [[RNNotificationsBridgeQueue sharedInstance] consumeNotificationsQueue:^(NSDictionary* notification) { - [RNNotifications didReceiveNotificationOnBackgroundState:notification]; - }]; - - // Push opened local notifications - NSDictionary* openedLocalNotification = [RNNotificationsBridgeQueue sharedInstance].openedLocalNotification; - if (openedLocalNotification) { - [RNNotificationsBridgeQueue sharedInstance].openedLocalNotification = nil; - [RNNotifications didNotificationOpen:openedLocalNotification]; - } - - // Push opened remote notifications - NSDictionary* openedRemoteNotification = [RNNotificationsBridgeQueue sharedInstance].openedRemoteNotification; - if (openedRemoteNotification) { - [RNNotificationsBridgeQueue sharedInstance].openedRemoteNotification = nil; - [RNNotifications didNotificationOpen:openedRemoteNotification]; - } -} - -RCT_EXPORT_METHOD(localNotification:(NSDictionary *)notification withId:(NSString *)notificationId) -{ - if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"10")) { - UNNotificationRequest* localNotification = [RCTConvert UNNotificationRequest:notification withId:notificationId]; - [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:localNotification withCompletionHandler:nil]; - } else { - UILocalNotification* localNotification = [RCTConvert UILocalNotification:notification]; - NSMutableArray* userInfo = localNotification.userInfo.mutableCopy; - [userInfo setValue:notificationId forKey:@"__id"]; - localNotification.userInfo = userInfo; - - if ([notification objectForKey:@"fireDate"] != nil) { - [[UIApplication sharedApplication] scheduleLocalNotification:localNotification]; - } else { - [[UIApplication sharedApplication] presentLocalNotificationNow:localNotification]; - } - } -} - -RCT_EXPORT_METHOD(cancelLocalNotification:(NSString *)notificationId) -{ - if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"10")) { - UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; - [center removePendingNotificationRequestsWithIdentifiers:@[notificationId]]; - } else { - for (UILocalNotification* notification in [UIApplication sharedApplication].scheduledLocalNotifications) { - NSDictionary* notificationInfo = notification.userInfo; - - if ([[notificationInfo objectForKey:@"__id"] isEqualToString:notificationId]) { - [[UIApplication sharedApplication] cancelLocalNotification:notification]; - } - } - } -} - -RCT_EXPORT_METHOD(cancelAllLocalNotifications) -{ - [RCTSharedApplication() cancelAllLocalNotifications]; -} - -RCT_EXPORT_METHOD(isRegisteredForRemoteNotifications:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) -{ - BOOL ans; - - if (TARGET_IPHONE_SIMULATOR) { - ans = [[[UIApplication sharedApplication] currentUserNotificationSettings] types] != 0; - } - else { - ans = [[UIApplication sharedApplication] isRegisteredForRemoteNotifications]; - } - resolve(@(ans)); -} - -RCT_EXPORT_METHOD(checkPermissions:(RCTPromiseResolveBlock) resolve - reject:(RCTPromiseRejectBlock) reject) { - UIUserNotificationSettings *currentSettings = [[UIApplication sharedApplication] currentUserNotificationSettings]; - resolve(@{ - @"badge": @((currentSettings.types & UIUserNotificationTypeBadge) > 0), - @"sound": @((currentSettings.types & UIUserNotificationTypeSound) > 0), - @"alert": @((currentSettings.types & UIUserNotificationTypeAlert) > 0), - }); -} - -#if !TARGET_OS_TV - -RCT_EXPORT_METHOD(removeAllDeliveredNotifications) -{ - if ([UNUserNotificationCenter class]) { - UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; - [center removeAllDeliveredNotifications]; - } -} - -RCT_EXPORT_METHOD(removeDeliveredNotifications:(NSArray *)identifiers) -{ - if ([UNUserNotificationCenter class]) { - UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; - [center removeDeliveredNotificationsWithIdentifiers:identifiers]; - } -} - -RCT_EXPORT_METHOD(getDeliveredNotifications:(RCTResponseSenderBlock)callback) -{ - if ([UNUserNotificationCenter class]) { - UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; - [center getDeliveredNotificationsWithCompletionHandler:^(NSArray * _Nonnull notifications) { - NSMutableArray *formattedNotifications = [NSMutableArray new]; - - for (UNNotification *notification in notifications) { - [formattedNotifications addObject:RCTFormatUNNotification(notification)]; - } - callback(@[formattedNotifications]); - }]; - } -} - -#endif !TARGET_OS_TV - -@end diff --git a/RNNotifications/RNNotifications.xcodeproj/project.pbxproj b/RNNotifications/RNNotifications.xcodeproj/project.pbxproj deleted file mode 100644 index 761eb17d0e7b357ae2f91fcb63cfd9d21e03e78a..0000000000000000000000000000000000000000 --- a/RNNotifications/RNNotifications.xcodeproj/project.pbxproj +++ /dev/null @@ -1,258 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - D85B37451CC05A0900DE9EB6 /* RNNotificationsBridgeQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = D85B37441CC05A0900DE9EB6 /* RNNotificationsBridgeQueue.m */; }; - D8A2F7551CB57F1A002CC8F5 /* RNNotifications.m in Sources */ = {isa = PBXBuildFile; fileRef = D8A2F7541CB57F1A002CC8F5 /* RNNotifications.m */; }; -/* End PBXBuildFile section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 58B511D91A9E6C8500147676 /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = "include/$(PRODUCT_NAME)"; - dstSubfolderSpec = 16; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 134814201AA4EA6300B7C361 /* libRNNotifications.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNNotifications.a; sourceTree = BUILT_PRODUCTS_DIR; }; - D85B37441CC05A0900DE9EB6 /* RNNotificationsBridgeQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNNotificationsBridgeQueue.m; sourceTree = ""; }; - D85B37461CC05A1200DE9EB6 /* RNNotificationsBridgeQueue.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNotificationsBridgeQueue.h; sourceTree = ""; }; - D8A2F7541CB57F1A002CC8F5 /* RNNotifications.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNNotifications.m; sourceTree = ""; }; - D8A2F7561CB57F28002CC8F5 /* RNNotifications.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNNotifications.h; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 58B511D81A9E6C8500147676 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 134814211AA4EA7D00B7C361 /* Products */ = { - isa = PBXGroup; - children = ( - 134814201AA4EA6300B7C361 /* libRNNotifications.a */, - ); - name = Products; - sourceTree = ""; - }; - 58B511D21A9E6C8500147676 = { - isa = PBXGroup; - children = ( - D85B37461CC05A1200DE9EB6 /* RNNotificationsBridgeQueue.h */, - D85B37441CC05A0900DE9EB6 /* RNNotificationsBridgeQueue.m */, - D8A2F7561CB57F28002CC8F5 /* RNNotifications.h */, - D8A2F7541CB57F1A002CC8F5 /* RNNotifications.m */, - 134814211AA4EA7D00B7C361 /* Products */, - ); - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 58B511DA1A9E6C8500147676 /* RNNotifications */ = { - isa = PBXNativeTarget; - buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNNotifications" */; - buildPhases = ( - 58B511D71A9E6C8500147676 /* Sources */, - 58B511D81A9E6C8500147676 /* Frameworks */, - 58B511D91A9E6C8500147676 /* CopyFiles */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = RNNotifications; - productName = RCTDataManager; - productReference = 134814201AA4EA6300B7C361 /* libRNNotifications.a */; - productType = "com.apple.product-type.library.static"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 58B511D31A9E6C8500147676 /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 0610; - ORGANIZATIONNAME = Facebook; - TargetAttributes = { - 58B511DA1A9E6C8500147676 = { - CreatedOnToolsVersion = 6.1.1; - }; - }; - }; - buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNNotifications" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - ); - mainGroup = 58B511D21A9E6C8500147676; - productRefGroup = 58B511D21A9E6C8500147676; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 58B511DA1A9E6C8500147676 /* RNNotifications */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXSourcesBuildPhase section */ - 58B511D71A9E6C8500147676 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - D8A2F7551CB57F1A002CC8F5 /* RNNotifications.m in Sources */, - D85B37451CC05A0900DE9EB6 /* RNNotificationsBridgeQueue.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin XCBuildConfiguration section */ - 58B511ED1A9E6C8500147676 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 7.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - }; - name = Debug; - }; - 58B511EE1A9E6C8500147676 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = YES; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 7.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 58B511F01A9E6C8500147676 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = YES; - HEADER_SEARCH_PATHS = ( - "$(SRCROOT)/../example/node_modules/react-native/React/**", - "$(SRCROOT)/../../react-native/React/**", - "$(SRCROOT)/../../../React/**", - ); - LIBRARY_SEARCH_PATHS = ""; - OTHER_LDFLAGS = "-ObjC"; - PRODUCT_NAME = RNNotifications; - SKIP_INSTALL = YES; - }; - name = Debug; - }; - 58B511F11A9E6C8500147676 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = YES; - HEADER_SEARCH_PATHS = ( - "$(SRCROOT)/../example/node_modules/react-native/React/**", - "$(SRCROOT)/../../react-native/React/**", - "$(SRCROOT)/../../../React/**", - ); - LIBRARY_SEARCH_PATHS = ""; - OTHER_LDFLAGS = "-ObjC"; - PRODUCT_NAME = RNNotifications; - SKIP_INSTALL = YES; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNNotifications" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 58B511ED1A9E6C8500147676 /* Debug */, - 58B511EE1A9E6C8500147676 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNNotifications" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 58B511F01A9E6C8500147676 /* Debug */, - 58B511F11A9E6C8500147676 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 58B511D31A9E6C8500147676 /* Project object */; -} diff --git a/RNNotifications/RNNotificationsBridgeQueue.h b/RNNotifications/RNNotificationsBridgeQueue.h deleted file mode 100644 index 61ff05ba57e5baf5ea3570bfd44ea6924d892f17..0000000000000000000000000000000000000000 --- a/RNNotifications/RNNotificationsBridgeQueue.h +++ /dev/null @@ -1,19 +0,0 @@ -#import - -@interface RNNotificationsBridgeQueue : NSObject - -@property BOOL jsIsReady; -@property NSDictionary* openedRemoteNotification; -@property NSDictionary* openedLocalNotification; - -+ (nonnull instancetype)sharedInstance; - -- (void)postAction:(NSDictionary *)action withCompletionKey:(NSString *)completionKey andCompletionHandler:(void (^)())completionHandler; -- (void)postNotification:(NSDictionary *)notification; - -- (void)consumeActionsQueue:(void (^)(NSDictionary *))block; -- (void)consumeNotificationsQueue:(void (^)(NSDictionary *))block; - -- (void)completeAction:(NSString *)completionKey; - -@end \ No newline at end of file diff --git a/RNNotifications/RNNotificationsBridgeQueue.m b/RNNotifications/RNNotificationsBridgeQueue.m deleted file mode 100644 index 9555f3fb457a02bd88215dc5671e398c92ff3eb9..0000000000000000000000000000000000000000 --- a/RNNotifications/RNNotificationsBridgeQueue.m +++ /dev/null @@ -1,95 +0,0 @@ -#import "RNNotificationsBridgeQueue.h" - -@implementation RNNotificationsBridgeQueue - -NSMutableArray* actionsQueue; -NSMutableArray* notificationsQueue; -NSMutableDictionary* actionCompletionHandlers; - -+ (nonnull instancetype)sharedInstance { - static RNNotificationsBridgeQueue* sharedInstance = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - sharedInstance = [self new]; - }); - - return sharedInstance; -} - -- (instancetype)init -{ - actionsQueue = [NSMutableArray new]; - notificationsQueue = [NSMutableArray new]; - actionCompletionHandlers = [NSMutableDictionary new]; - self.jsIsReady = NO; - - return self; -} - -- (void)postNotification:(NSDictionary *)notification -{ - if (!notificationsQueue) return; - [notificationsQueue insertObject:notification atIndex:0]; -} - -- (NSDictionary *)dequeueSingleNotification -{ - if (!notificationsQueue || notificationsQueue.count == 0) return nil; - - NSDictionary* notification = [notificationsQueue lastObject]; - [notificationsQueue removeLastObject]; - - return notification; -} - -- (void)consumeNotificationsQueue:(void (^)(NSDictionary *))block -{ - NSDictionary* notification; - - while ((notification = [self dequeueSingleNotification]) != nil) { - block(notification); - } - - notificationsQueue = nil; -} - -- (void)postAction:(NSDictionary *)action withCompletionKey:(NSString *)completionKey andCompletionHandler:(void (^)())completionHandler -{ - // store completion handler - actionCompletionHandlers[completionKey] = completionHandler; - - if (!actionsQueue) return; - [actionsQueue insertObject:action atIndex:0]; -} - -- (NSDictionary *)dequeueSingleAction -{ - if (!actionsQueue || actionsQueue.count == 0) return nil; - - NSDictionary* action = [actionsQueue lastObject]; - [actionsQueue removeLastObject]; - - return action; -} - -- (void)consumeActionsQueue:(void (^)(NSDictionary *))block -{ - NSDictionary* lastActionInfo; - - while ((lastActionInfo = [self dequeueSingleAction]) != nil) { - block(lastActionInfo); - } - - actionsQueue = nil; -} - -- (void)completeAction:(NSString *)completionKey -{ - void (^completionHandler)() = (void (^)())[actionCompletionHandlers valueForKey:completionKey]; - if (completionHandler) { - completionHandler(); - [actionCompletionHandlers removeObjectForKey:completionKey]; - } -} - -@end \ No newline at end of file diff --git a/android/.gitignore b/android/.gitignore deleted file mode 100644 index 796b96d1c402326528b4ba3c12ee9d92d0e212e9..0000000000000000000000000000000000000000 --- a/android/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build diff --git a/android/app/src/reactNative59/java/com/wix/reactnativenotifications/NotificationManagerCompatFacade.java b/android/app/src/reactNative59/java/com/wix/reactnativenotifications/NotificationManagerCompatFacade.java new file mode 100644 index 0000000000000000000000000000000000000000..f9c858b068b3f3dd29c5e0ab9c1aa3efcf7f88e0 --- /dev/null +++ b/android/app/src/reactNative59/java/com/wix/reactnativenotifications/NotificationManagerCompatFacade.java @@ -0,0 +1,12 @@ + +package com.wix.reactnativenotifications; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.support.v4.app.NotificationManagerCompat; + +public abstract class NotificationManagerCompatFacade { + public static NotificationManagerCompat from(@NonNull Context context) { + return NotificationManagerCompat.from(context); + } +} diff --git a/android/app/src/reactNative60/java/com/wix/reactnativenotifications/NotificationManagerCompatFacade.java b/android/app/src/reactNative60/java/com/wix/reactnativenotifications/NotificationManagerCompatFacade.java new file mode 100644 index 0000000000000000000000000000000000000000..94ea1884c8d84d6d81efeb8ebe91dacf41d2622b --- /dev/null +++ b/android/app/src/reactNative60/java/com/wix/reactnativenotifications/NotificationManagerCompatFacade.java @@ -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); + } +} diff --git a/android/build.gradle b/android/build.gradle deleted file mode 100644 index de0db35f9d7c22a6d8a179f04fc39dafa1275214..0000000000000000000000000000000000000000 --- a/android/build.gradle +++ /dev/null @@ -1,30 +0,0 @@ -apply plugin: 'com.android.library' - -android { - compileSdkVersion 27 - buildToolsVersion '28.0.3' - - defaultConfig { - minSdkVersion 19 - targetSdkVersion 27 - versionCode 1 - versionName "1.0" - } - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } -} - -dependencies { - // Google's GCM. - compile 'com.google.android.gms:play-services-gcm:15.0.1' - - compile 'com.facebook.react:react-native:+' - - testCompile 'junit:junit:4.12' - testCompile 'org.mockito:mockito-core:2.+' - testCompile 'org.robolectric:robolectric:3.1.4' -} diff --git a/android/reactnativenotification.iml b/android/reactnativenotification.iml deleted file mode 100644 index c77c19bda1ae6092c00391be4c9fdbc5313e414b..0000000000000000000000000000000000000000 --- a/android/reactnativenotification.iml +++ /dev/null @@ -1,154 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml deleted file mode 100644 index 14334752383c271cef67a598c6282e09dcbd8885..0000000000000000000000000000000000000000 --- a/android/src/main/AndroidManifest.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/android/src/main/java/com/wix/reactnativenotifications/RNNotificationsPackage.java b/android/src/main/java/com/wix/reactnativenotifications/RNNotificationsPackage.java deleted file mode 100644 index 417e12b6417c2d4c40799c662047c0cd3f8e6402..0000000000000000000000000000000000000000 --- a/android/src/main/java/com/wix/reactnativenotifications/RNNotificationsPackage.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.wix.reactnativenotifications; - -import android.app.Application; - -import com.facebook.react.ReactPackage; -import com.facebook.react.bridge.NativeModule; -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.uimanager.ViewManager; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -public class RNNotificationsPackage implements ReactPackage { - - private final Application mApplication; - - public RNNotificationsPackage(Application application) { - mApplication = application; - } - - @Override - public List createNativeModules(ReactApplicationContext reactContext) { - return Arrays.asList(new RNNotificationsModule(mApplication, reactContext)); - } - - @Override - public List createViewManagers(ReactApplicationContext reactContext) { - return Collections.emptyList(); - } -} diff --git a/android/src/main/java/com/wix/reactnativenotifications/gcm/GcmInstanceIdListenerService.java b/android/src/main/java/com/wix/reactnativenotifications/gcm/GcmInstanceIdListenerService.java deleted file mode 100644 index 933415f5cdc3b96b055c2a5e1c76208e914dc9b6..0000000000000000000000000000000000000000 --- a/android/src/main/java/com/wix/reactnativenotifications/gcm/GcmInstanceIdListenerService.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.wix.reactnativenotifications.gcm; - -import android.content.Intent; - -import com.google.android.gms.iid.InstanceIDListenerService; - -/** - * Instance-ID + token refreshing handling service. Contacts the GCM to fetch the updated token. - * - * @author amitd - */ -public class GcmInstanceIdListenerService extends InstanceIDListenerService { - - @Override - public void onTokenRefresh() { - // Fetch updated Instance ID token and notify our app's server of any changes (if applicable). - // Google recommends running this from an intent service. - Intent intent = new Intent(this, GcmInstanceIdRefreshHandlerService.class); - startService(intent); - } -} diff --git a/android/src/main/java/com/wix/reactnativenotifications/gcm/GcmMessageHandlerService.java b/android/src/main/java/com/wix/reactnativenotifications/gcm/GcmMessageHandlerService.java deleted file mode 100644 index f59665529429b87779fc37fe009f7505b30bc394..0000000000000000000000000000000000000000 --- a/android/src/main/java/com/wix/reactnativenotifications/gcm/GcmMessageHandlerService.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.wix.reactnativenotifications.gcm; - -import android.os.Bundle; -import android.util.Log; - -import com.google.android.gms.gcm.GcmListenerService; -import com.wix.reactnativenotifications.core.notification.IPushNotification; -import com.wix.reactnativenotifications.core.notification.PushNotification; - -import static com.wix.reactnativenotifications.Defs.LOGTAG; - -public class GcmMessageHandlerService extends GcmListenerService { - - @Override - public void onMessageReceived(String s, Bundle bundle) { - Log.d(LOGTAG, "New message from GCM: " + bundle); - - try { - final IPushNotification notification = PushNotification.get(getApplicationContext(), bundle); - notification.onReceived(); - } catch (IPushNotification.InvalidNotificationException e) { - // A GCM message, yes - but not the kind we know how to work with. - Log.v(LOGTAG, "GCM message handling aborted", e); - } - } -} diff --git a/android/src/main/java/com/wix/reactnativenotifications/gcm/GcmToken.java b/android/src/main/java/com/wix/reactnativenotifications/gcm/GcmToken.java deleted file mode 100644 index b11a6b5f4a6a89342d5d684bd0ed2d7950ec5991..0000000000000000000000000000000000000000 --- a/android/src/main/java/com/wix/reactnativenotifications/gcm/GcmToken.java +++ /dev/null @@ -1,132 +0,0 @@ -package com.wix.reactnativenotifications.gcm; - -import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.support.annotation.NonNull; -import android.util.Log; - -import com.facebook.react.ReactApplication; -import com.facebook.react.ReactInstanceManager; -import com.facebook.react.bridge.ReactContext; -import com.facebook.react.modules.core.DeviceEventManagerModule; -import com.google.android.gms.gcm.GoogleCloudMessaging; -import com.google.android.gms.iid.InstanceID; - -import static com.wix.reactnativenotifications.Defs.GCM_SENDER_ID_ATTR_NAME; -import static com.wix.reactnativenotifications.Defs.LOGTAG; -import static com.wix.reactnativenotifications.Defs.TOKEN_RECEIVED_EVENT_NAME; - -public class GcmToken implements IGcmToken { - - final protected Context mAppContext; - - protected static String sToken; - - protected GcmToken(Context appContext) { - if (!(appContext instanceof ReactApplication)) { - throw new IllegalStateException("Application instance isn't a react-application"); - } - mAppContext = appContext; - } - - public static IGcmToken get(Context context) { - Context appContext = context.getApplicationContext(); - if (appContext instanceof INotificationsGcmApplication) { - return ((INotificationsGcmApplication) appContext).getGcmToken(context); - } - return new GcmToken(appContext); - } - - @Override - public void onNewTokenReady() { - synchronized (mAppContext) { - refreshToken(); - } - } - - @Override - public void onManualRefresh() { - synchronized (mAppContext) { - if (sToken == null) { - Log.i(LOGTAG, "Manual token refresh => asking for new token"); - refreshToken(); - } else { - Log.i(LOGTAG, "Manual token refresh => publishing existing token ("+sToken+")"); - sendTokenToJS(); - } - } - } - - @Override - public void onAppReady() { - synchronized (mAppContext) { - if (sToken == null) { - Log.i(LOGTAG, "App initialized => asking for new token"); - refreshToken(); - } else { - // Except for first run, this should be the case. - Log.i(LOGTAG, "App initialized => publishing existing token ("+sToken+")"); - sendTokenToJS(); - } - } - } - - protected void refreshToken() { - try { - sToken = getNewToken(); - } catch (Exception e) { - Log.e(LOGTAG, "Failed to retrieve new token", e); - return; - } - - sendTokenToJS(); - } - - @NonNull - protected String getNewToken() throws Exception { - final InstanceID instanceId = InstanceID.getInstance(mAppContext); - Log.d(LOGTAG, "GCM is refreshing token... instanceId=" + instanceId.getId()); - - // TODO why is this needed? - GoogleCloudMessaging.getInstance(mAppContext).close(); - - try { - final String registrationToken = instanceId.getToken(getSenderId(), GoogleCloudMessaging.INSTANCE_ID_SCOPE); - Log.i(LOGTAG, "GCM has a new token: instanceId=" + instanceId.getId() + ", token=" + registrationToken); - return registrationToken; - } catch (Exception e) { - throw new Exception("FATAL: Failed to fetch a fresh new token, instanceId=" + instanceId.getId(), e); - } - } - - protected String getSenderId() { - final String senderId = getSenderIdFromManifest(); - if (senderId == null) { - throw new IllegalStateException("Sender ID not found in manifest. Did you forget to add it as the value of a '"+GCM_SENDER_ID_ATTR_NAME+"' meta-data field?"); - } - return senderId; - } - - protected String getSenderIdFromManifest() { - final ApplicationInfo appInfo; - try { - appInfo = mAppContext.getPackageManager().getApplicationInfo(mAppContext.getPackageName(), PackageManager.GET_META_DATA); - return appInfo.metaData.getString(GCM_SENDER_ID_ATTR_NAME); - } catch (PackageManager.NameNotFoundException e) { - // Should REALLY never happen cause we're querying for our own package. - Log.e(LOGTAG, "Failed to resolve sender ID from manifest", e); - return null; - } - } - - protected void sendTokenToJS() { - final ReactInstanceManager instanceManager = ((ReactApplication) mAppContext).getReactNativeHost().getReactInstanceManager(); - final ReactContext reactContext = instanceManager.getCurrentReactContext(); - - // Note: Cannot assume react-context exists cause this is an async dispatched service. - if (reactContext != null && reactContext.hasActiveCatalystInstance()) { - reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(TOKEN_RECEIVED_EVENT_NAME, sToken); - } - } -} diff --git a/android/src/main/java/com/wix/reactnativenotifications/gcm/INotificationsGcmApplication.java b/android/src/main/java/com/wix/reactnativenotifications/gcm/INotificationsGcmApplication.java deleted file mode 100644 index 36f59f71cf2e4682d409c1fd21ac07a333d66a11..0000000000000000000000000000000000000000 --- a/android/src/main/java/com/wix/reactnativenotifications/gcm/INotificationsGcmApplication.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.wix.reactnativenotifications.gcm; - -import android.content.Context; - -public interface INotificationsGcmApplication { - IGcmToken getGcmToken(Context context); -} diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 0000000000000000000000000000000000000000..f25946816ed74ef62f5963a02af894c66d23227b --- /dev/null +++ b/babel.config.js @@ -0,0 +1,12 @@ +module.exports = function (api) { + api && api.cache(false); + return { + presets: [ + 'module:metro-react-native-babel-preset' + ], + plugins: [ + '@babel/plugin-proposal-export-namespace-from', + '@babel/plugin-proposal-export-default-from' + ] + }; +}; diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100755 index 0000000000000000000000000000000000000000..6711192ae1e76330e22e44d528c5721369075269 --- /dev/null +++ b/docker-compose.yml @@ -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 diff --git a/docs/advanced-ios.md b/docs/advanced-ios.md new file mode 100644 index 0000000000000000000000000000000000000000..4dd22babb6e826c515a10279defbd4cf59ffd005 --- /dev/null +++ b/docs/advanced-ios.md @@ -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.getBadgesCount((count) => console.log(count)); +``` + +Set to specific number: +```javascript +Notifications.ios.setBadgesCount(2); +``` +Clear badges icon: +```javascript +Notifications.ios.setBadgesCount(0); +``` diff --git a/docs/android-api.md b/docs/android-api.md new file mode 100755 index 0000000000000000000000000000000000000000..9ee6f98711277bddac9fe87b740ae0da8e32ecac --- /dev/null +++ b/docs/android-api.md @@ -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(); +``` diff --git a/docs/general-api.md b/docs/general-api.md new file mode 100755 index 0000000000000000000000000000000000000000..1d536f972d67c3fd66c91941b65a540f5657c152 --- /dev/null +++ b/docs/general-api.md @@ -0,0 +1,48 @@ +--- +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(); +``` diff --git a/docs/general-events.md b/docs/general-events.md new file mode 100755 index 0000000000000000000000000000000000000000..d1e5a55d76944dd82e72c786b6a5afe679bd3b7d --- /dev/null +++ b/docs/general-events.md @@ -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); +}); +``` \ No newline at end of file diff --git a/docs/installation-android.md b/docs/installation-android.md new file mode 100755 index 0000000000000000000000000000000000000000..58bdc707a7a89592fc4c3a04dd72948e5eb03330 --- /dev/null +++ b/docs/installation-android.md @@ -0,0 +1,58 @@ +--- +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 getPackages() { + return Arrays.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' +``` diff --git a/docs/installation-ios.md b/docs/installation-ios.md new file mode 100755 index 0000000000000000000000000000000000000000..0e43f5e677f703f98c574d7a98836a85eb79fa94 --- /dev/null +++ b/docs/installation-ios.md @@ -0,0 +1,50 @@ +--- +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. + +The 2nd is to do some platform specific setup so as to be able to work with Apple and Google's services for push notifications. + +Start by running this: + +``` +$ npm install react-native-notifications --save +``` + + +Then, [Manually link](https://facebook.github.io/react-native/docs/linking-libraries-ios.html#manual-linking) the library to your Xcode project. + + +To enable notifications support 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]; +} \ No newline at end of file diff --git a/docs/installation.md b/docs/installation.md deleted file mode 100644 index 12e33d02f92935ae76e33dda9d687980e06d4de5..0000000000000000000000000000000000000000 --- a/docs/installation.md +++ /dev/null @@ -1,117 +0,0 @@ -# Installation - -As with any React Native project, the first step is to add the project as an npm dependency. - -The 2nd is to do some platform specific setup so as to be able to work with Apple and Google's services for push notifications. - -Start by running this: - -``` -$ npm install react-native-notifications --save -``` - -## iOS - -First, [Manually link](https://facebook.github.io/react-native/docs/linking-libraries-ios.html#manual-linking) the library to your Xcode project. - -Then, to enable notifications support add the following line at the top of your `AppDelegate.m` - -```objective-c -#import "RNNotifications.h" -``` - -And the following methods to support registration and receiving notifications: - -```objective-c -// Required to register for notifications -- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings -{ - [RNNotifications didRegisterUserNotificationSettings:notificationSettings]; -} - -- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken -{ - [RNNotifications didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; -} - -- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { - [RNNotifications didFailToRegisterForRemoteNotificationsWithError:error]; -} - -// Required for the notification event. -- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)notification { - [RNNotifications didReceiveRemoteNotification:notification]; -} - -// Required for the localNotification event. -- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification -{ - [RNNotifications didReceiveLocalNotification:notification]; -} -``` - -## Android - - -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') -``` - -Declare the library as a dependency in your **app-project's** `build.gradle`: - -```gradle -dependencies { - // ... - - compile project(':reactnativenotifications') -} -``` - -Add the library to your application class (e.g. `MainApplication.java`): - -```java -import com.wix.reactnativenotifications.RNNotificationsPackage; - -... - - @Override - protected List getPackages() { - return Arrays.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 GCM service](https://developers.google.com/cloud-messaging/gcm) (now integrated into Firebase). The following installation steps are a TL;DR of [Google's GCM setup guide](https://developers.google.com/cloud-messaging/android/client). You can follow them to get GCM 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 GCM - -To set GCM in your app, you must first create a Google API-project and obtain a **Sender ID** and a **Server API Key**. 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://developers.google.com/mobile/add); Use [this tutorial](https://code.tutsplus.com/tutorials/how-to-get-started-with-push-notifications-on-android--cms-25870) for insturctions. - -Alternatively, follow [Google's complete guide](https://developers.google.com/cloud-messaging/android/client#create-an-api-project). - -#### Step #2: Add Sender ID to Manifest File - -Once obtained, bundle the Sender ID onto your main `manifest.xml` file: - -```gradle - -... - - ... - // Replace '1234567890' with your sender ID. - // IMPORTANT: Leave the trailing \0 intact!!! - - - - -``` diff --git a/docs/ios-api.md b/docs/ios-api.md new file mode 100755 index 0000000000000000000000000000000000000000..bc1b8d1c867d9e3dfec0645f6330094821b703a4 --- /dev/null +++ b/docs/ios-api.md @@ -0,0 +1,77 @@ +--- +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 app’s notifications that are still displayed in Notification Center + +```js +Notifications.ios.getDeliveredNotifications(); +``` + +## removeAllDeliveredNotifications() +Remove all delivered notifications from Notification Center + +```js +Notifications.ios.removeAllDeliveredNotifications(); +``` + +## 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); +``` \ No newline at end of file diff --git a/docs/ios-events.md b/docs/ios-events.md new file mode 100755 index 0000000000000000000000000000000000000000..8a939194ba782f7c40d3527053d1a49df1b19b16 --- /dev/null +++ b/docs/ios-events.md @@ -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)); +}); +``` + diff --git a/docs/localNotifications.md b/docs/localNotifications.md index 9747146c6d99bec2f91fa6573abe2075ffb5b486..db3b6b1e7b52293eac0788fbc67ecb93b7f3102e 100644 --- a/docs/localNotifications.md +++ b/docs/localNotifications.md @@ -1,7 +1,10 @@ +--- +id: localNotifications +title: Local Notifications +sidebar_label: Local Notifications +--- -# Local Notifications - -## iOS + 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,10 +12,10 @@ Triggering local notifications is fully compatible with React Native `PushNotifi Example: ```javascript -let localNotification = NotificationsIOS.localNotification({ - alertBody: "Local notificiation!", - alertTitle: "Local Notification Title", - soundName: "chime.aiff", +let localNotification = Notifications.postLocalNotification({ + body: "Local notificiation!", + title: "Local Notification Title", + sound: "chime.aiff", silent: false, category: "SOME_CATEGORY", userInfo: { } @@ -22,46 +25,46 @@ let localNotification = NotificationsIOS.localNotification({ Notification object contains: - **`fireDate`**- The date and time when the system should deliver the notification (optinal - default is immidiate dispatch). -- `alertBody`- The message displayed in the notification alert. -- `alertTitle`- The title of the notification, displayed in the notifications center. +- `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. -- `soundName`- 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. +- `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)`. +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({ - alertBody: "Local notificiation!", - alertTitle: "Local Notification Title", - soundName: "chime.aiff", +let someLocalNotification = Notifications.postLocalNotification({ + body: "Local notificiation!", + title: "Local Notification Title", + sound: "chime.aiff", category: "SOME_CATEGORY", 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). -## Android + 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" diff --git a/docs/notification-object.md b/docs/notification-object.md new file mode 100755 index 0000000000000000000000000000000000000000..da676260f8458b5cc303dcfb2f72f5c9313f1f56 --- /dev/null +++ b/docs/notification-object.md @@ -0,0 +1,23 @@ +--- +id: notification-object +title: Notification object +sidebar_label: Notification +--- + +Contains the payload data. + +- **`message`**- 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). +- **`data`**- returns the data payload (additional info) of the notification. + +Example: +```js +Notifications.events().registerNotificationReceived((notification: Notification, completion: (response: NotificationCompletion) => void) => { + // Prints the notification payload + console.log(JSON.stringify(notification.data)); + + completion({alert: false, sound: false, badge: false}); +}); +``` \ No newline at end of file diff --git a/docs/notifications-events.md b/docs/notifications-events.md new file mode 100644 index 0000000000000000000000000000000000000000..a969868f1eaf2d0c3540325cc5b12d8047ad6396 --- /dev/null +++ b/docs/notifications-events.md @@ -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. diff --git a/docs/subscription.md b/docs/subscription.md index ddd493e62ea18a9a04455863aeb8b5780e0fcf3a..1b5038c6dbd1e7cbc60b656e0242e18c070edf8b 100644 --- a/docs/subscription.md +++ b/docs/subscription.md @@ -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. -## 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); - } - - 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.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); + }); + + 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); }); ``` - - -## 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. diff --git a/docs/advancedIos.md b/docs_old/advancedIos.md similarity index 75% rename from docs/advancedIos.md rename to docs_old/advancedIos.md index 9e44bde671158c2b25eea0a66aa3a593d7d2cdca..27f69ce15383f6279500a871f1854a0be6d2e547 100644 --- a/docs/advancedIos.md +++ b/docs_old/advancedIos.md @@ -91,33 +91,31 @@ After [preparing your app to receive VoIP push notifications](https://developer. #import ``` -And the following methods: +### Listen to PushKit notifications +On receiving PushKit notification, a `pushKitNotificationReceived` event will be fired with the notification payload. ```objective-c -// PushKit API Support -- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSString *)type -{ - [RNNotifications didUpdatePushCredentials:credentials forType:type]; -} - -- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type -{ - [RNNotifications didReceiveRemoteNotification:payload.dictionaryPayload]; -} -``` +#import "RNNotifications.h" +#import +``` In your ReactNative code, add event handler for `pushKitRegistered` event and call to `registerPushKit()`: ```javascript constructor() { - NotificationsIOS.addEventListener('pushKitRegistered', this.onPushKitRegistered.bind(this)); - NotificationsIOS.registerPushKit(); + NotificationsIOS.addEventListener('pushKitRegistered', this.onPushKitRegistered.bind(this)); + NotificationsIOS.addEventListener('pushKitNotificationReceived', this.onPushKitNotificationReceived.bind(this)); + NotificationsIOS.registerPushKit(); } onPushKitRegistered(deviceToken) { console.log("PushKit Token Received: " + deviceToken); } +onPushKitNotificationReceived(notification) { + console.log('PushKit notification Received: ' + JSON.stringify(notification)); +} + componentWillUnmount() { // Don't forget to remove the event listeners to prevent memory leaks! NotificationsIOS.removeEventListener('pushKitRegistered', onPushKitRegistered(this)); @@ -150,22 +148,7 @@ 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. -In order to support interactive notifications, firstly add the following methods to `appDelegate.m` file: - -```objective-c -// Required for the notification actions. -- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler -{ - [RNNotifications handleActionWithIdentifier:identifier forLocalNotification:notification withResponseInfo:responseInfo completionHandler:completionHandler]; -} - -- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler -{ - [RNNotifications handleActionWithIdentifier:identifier forRemoteNotification:userInfo withResponseInfo:responseInfo completionHandler:completionHandler]; -} -``` - -Then, follow the basic workflow of adding interactive notifications to your app: +Follow the basic workflow of adding interactive notifications to your app: 1. Config the actions. 2. Group actions together into categories. @@ -182,26 +165,18 @@ import NotificationsIOS, { NotificationAction, NotificationCategory } from 'reac let upvoteAction = new NotificationAction({ activationMode: "background", title: String.fromCodePoint(0x1F44D), - identifier: "UPVOTE_ACTION" -}, (action, completed) => { - console.log("ACTION RECEIVED"); - console.log(JSON.stringify(action)); - - // You must call to completed(), otherwise the action will not be triggered - completed(); + identifier: "UPVOTE_ACTION", + textInput: { + buttonTitle: 'title', + placeholder: 'placeholder text' + } }); let replyAction = new NotificationAction({ activationMode: "background", title: "Reply", - behavior: "textInput", authenticationRequired: true, identifier: "REPLY_ACTION" -}, (action, completed) => { - console.log("ACTION RECEIVED"); - console.log(action); - - completed(); }); ``` @@ -212,8 +187,7 @@ We will group `upvote` action and `reply` action into a single category: `EXAMPL ```javascript let exampleCategory = new NotificationCategory({ identifier: "EXAMPLE_CATEGORY", - actions: [upvoteAction, replyAction], - context: "default" + actions: [upvoteAction, replyAction] }); ``` @@ -230,8 +204,8 @@ Notification payload should look like this: ```javascript { aps: { - // ... (alert, sound, badge, etc) - category: "EXAMPLE_CATEGORY" + // ... (alert, sound, badge, etc) + category: "EXAMPLE_CATEGORY" } } ``` @@ -245,9 +219,7 @@ The [example app](https://github.com/wix/react-native-notifications/tree/master/ - `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. -- `behavior` - Indicating additional behavior that the action supports. - - `default` - No additional behavior. - - `textInput` - When button is tapped, the action opens a text input. the text will be delivered to your action callback. +- `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. @@ -255,9 +227,11 @@ The [example app](https://github.com/wix/react-native-notifications/tree/master/ - `identifier` - The name of the action group (must be unique). - `actions` - An array of `NotificationAction` objects, which related to this category. -- `context` - Indicating the amount of space available for displaying actions in a notification. - - `default` (default) - Displayes up to 4 actions (full UI). - - `minimal` - Displays up tp 2 actions (minimal UI). + +### `TextInput` Payload + +- `buttonTitle` - Title of the `send` button. +- `placeholder` - Placeholder for the `textInput`. #### Get and set application icon badges count (iOS only) diff --git a/docs_old/installation.md b/docs_old/installation.md new file mode 100644 index 0000000000000000000000000000000000000000..56c52d29a5ad758583c6f3853b268eba585883d7 --- /dev/null +++ b/docs_old/installation.md @@ -0,0 +1,186 @@ +# Installation + +As with any React Native project, the first step is to add the project as an npm dependency. + +The 2nd is to do some platform specific setup so as to be able to work with Apple and Google's services for push notifications. + +Start by running this: + +``` +$ npm install react-native-notifications@^2.0.6 --save +``` + +## iOS + +First, [Manually link](https://facebook.github.io/react-native/docs/linking-libraries-ios.html#manual-linking) the library to your Xcode project. + +Then, to enable notifications support 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]; +} + +``` + +## Android + + +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/lib/android/app') +``` + +Declare the library as a dependency in your **app-project's** `build.gradle`: + +```gradle +dependencies { + // ... + + compile project(':reactnativenotifications') +} +``` + +Add the library to your application class (e.g. `MainApplication.java`): + +```java +import com.wix.reactnativenotifications.RNNotificationsPackage; + +... + + @Override + protected List getPackages() { + return Arrays.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: RNNotifications and React Native version +This step is required only for `react-native-notifications` version `2.1.0` and above.
+ +react-native-notifications supports multiple React Native versions. Target the React Native version required by your project by specifying the RNN build flavor in `android/app/build.gradle`. + +```diff +android { + ... + defaultConfig { + applicationId "com.yourproject" + minSdkVersion rootProject.ext.minSdkVersion + targetSdkVersion rootProject.ext.targetSdkVersion ++ missingDimensionStrategy "RNNotifications.reactNativeVersion", "reactNative60" // See note below! + versionCode 1 + versionName "1.0" + ... + } + ... +} +``` + +!>Important note about `missingDimensionStrategy`
+>`reactNative59` - RN 0.59.x and below
+>`reactNative60` - RN 0.60.0 and above + +Now we need to instruct gradle how to build that flavor. To do so here two solutions: + +#### 5.1 Build app with gradle command + +**prefered solution** The RNNotification flavor you would like to build is specified in `app/build.gradle`. Therefore in order to compile only that flavor, instead of building your entire project using `./gradlew assembleDebug`, you should instruct gradle to build the app module: `./gradlew app:assembleDebug`. The easiest way is to add a package.json command to build and install your debug Android APK . + +``` +"scripts": { + ... + "android": "cd ./android && ./gradlew app:assembleDebug && ./gradlew installDebug" +} +``` + +Now run `npm run android` to build your application + +#### 5.2 Ignore other RNN flavors + +If you don't want to run `npm run android` and want to keep the default `react-native run-android` command, you need to specify to graddle to ignore the other flavors RNNotifications provides. + +To do so edit `android/build.gradle` and add: + +```diff ++subprojects { subproject -> ++ afterEvaluate { ++ if ((subproject.plugins.hasPlugin('android') || subproject.plugins.hasPlugin('android-library'))) { ++ android { ++ variantFilter { variant -> ++ def names = variant.flavors*.name ++ if (names.contains("reactNative59")) { ++ setIgnore(true) ++ } ++ } ++ } ++ } ++ } ++} +``` + +**Note**: As more build variants come available in the future, you will need to adjust the list (`names.contains("reactNative59")`). This is why we recommend the first solution. \ No newline at end of file diff --git a/docs_old/localNotifications.md b/docs_old/localNotifications.md new file mode 100644 index 0000000000000000000000000000000000000000..8f1d2b94838dec8baa1359ca8c0d56785289fb8d --- /dev/null +++ b/docs_old/localNotifications.md @@ -0,0 +1,84 @@ + +# Local Notifications + +## 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). + + +## 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). diff --git a/docs/notificationsEvents.md b/docs_old/notificationsEvents.md similarity index 76% rename from docs/notificationsEvents.md rename to docs_old/notificationsEvents.md index 1a1c0b975ba15bcfa0714c2d9b05c66e77b30a3b..21cdef361f174916dbfcc6851b4772fd37bdacc7 100644 --- a/docs/notificationsEvents.md +++ b/docs_old/notificationsEvents.md @@ -5,40 +5,35 @@ 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 `notificationReceivedForeground` event will be fired. -2. **Background:** When the app is running in a background state; in this case, a `notificationReceivedBackground` event will be fired. +1. **Forground:** When the app is running and is used by the user right now; in this case, a `notificationReceivedForeground` 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. +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() { this._boundOnNotificationReceivedForeground = this.onNotificationReceivedForeground.bind(this); - this._boundOnNotificationReceivedBackground = this.onNotificationReceivedBackground.bind(this); this._boundOnNotificationOpened = this.onNotificationOpened.bind(this); NotificationsIOS.addEventListener('notificationReceivedForeground', this._boundOnNotificationReceivedForeground); - NotificationsIOS.addEventListener('notificationReceivedBackground', this._boundOnNotificationReceivedBackground); NotificationsIOS.addEventListener('notificationOpened', this._boundOnNotificationOpened); } -onNotificationReceivedForeground(notification) { +onNotificationReceivedForeground(notification, completion) { + completion({alert: true, sound: false, badge: false}); console.log("Notification Received - Foreground", notification); } -onNotificationReceivedBackground(notification) { - console.log("Notification Received - Background", notification); -} - -onNotificationOpened(notification) { +onNotificationOpened(notification, completion, action) { console.log("Notification opened by device user", notification); + console.log(`Notification opened with an action identifier: ${action.identifier} and response text: ${action.text}`, notification); + completion(); } componentWillUnmount() { // Don't forget to remove the event listeners to prevent memory leaks! NotificationsIOS.removeEventListener('notificationReceivedForeground', this._boundOnNotificationReceivedForeground); - NotificationsIOS.removeEventListener('notificationReceivedBackground', this._boundOnNotificationReceivedBackground); NotificationsIOS.removeEventListener('notificationOpened', this._boundOnNotificationOpened); } ``` @@ -54,12 +49,6 @@ When you receive a push notification, you'll get an instance of `IOSNotification - **`getData()`**- returns the data payload (additional info) of the notification. - **`getType()`**- returns `managed` for managed notifications, otherwise returns `regular`. -### Background Queue (Important - please read!) - -When a push notification is opened but the app is not running, the application will be in a **cold launch** state, until the JS engine is up and ready to handle the notification. -The application will collect the events (notifications, actions, etc.) that happend during the cold launch for you. - -When your app is ready (most of the time it's after the call to `requestPermissions()`), just call to `NotificationsIOS.consumeBackgroundQueue();` in order to consume the background queue. For more info see `index.ios.js` in the example app. ## Android diff --git a/docs_old/subscription.md b/docs_old/subscription.md new file mode 100644 index 0000000000000000000000000000000000000000..ddd493e62ea18a9a04455863aeb8b5780e0fcf3a --- /dev/null +++ b/docs_old/subscription.md @@ -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. + +## 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); +}); +``` + + +## 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. diff --git a/e2e/Notifications.test.js b/e2e/Notifications.test.js new file mode 100644 index 0000000000000000000000000000000000000000..09881409dd16254d3375a86ca86660c67b069301 --- /dev/null +++ b/e2e/Notifications.test.js @@ -0,0 +1,56 @@ +const Utils = require('./Utils'); +const {elementByLabel} = Utils; + +describe('Notifications', () => { + beforeEach(async () => { + await device.relaunchApp({delete: true, permissions: {notifications: 'YES'}}); + }); + + describe('Foreground', () => { + it('Should receive notification', async () => { + await device.sendUserNotification(createNotification({link: 'foreground/notification'})); + await linkShouldBeVisible('foreground/notification'); + }); + + it('Should open notification', async () => { + await device.sendUserNotification(createNotification({link: 'foreground/notification/click', showAlert: true})); + await expect(elementByLabel('Notification Clicked: foreground/notification/click')).toBeVisible(); + }); + }); + + describe('Background', () => { + it('Should open notification', async () => { + await device.sendToHome(); + await expect(elementByLabel('Notification Clicked: background/notification')).toBeNotVisible(); + await device.launchApp({newInstance: false, userNotification: createNotification({link: 'background/notification'})}); + await expect(elementByLabel('Notification Clicked: background/notification')).toBeVisible(); + }); + }); + + describe('Dead state', () => { + it('Should receive notification', async () => { + await device.launchApp({newInstance: true, userNotification: createNotification({link: 'deadState/notification'})}); + await linkShouldBeVisible('deadState/notification'); + }); + }); + + async function linkShouldBeVisible(link) { + return await expect(elementByLabel(`Extra Link Param: ${link}`)).toBeVisible(); + } +}); + +function createNotification({link, showAlert}) { + return { + trigger: { + type: 'push' + }, + title: 'From push', + subtitle: 'Subtitle', + body: 'Body', + badge: 1, + payload: { + link, + showAlert + } + }; +} diff --git a/e2e/Utils.js b/e2e/Utils.js new file mode 100644 index 0000000000000000000000000000000000000000..cc3b76419956dfa5091e88e6f57c189cc7a4bd1a --- /dev/null +++ b/e2e/Utils.js @@ -0,0 +1,17 @@ + +module.exports = { + elementByLabel: (label) => { + return element(by.text(label)); + }, + elementById: (id) => { + return element(by.id(id)); + }, + tapBackIos: () => { + try { + return element(by.traits(['button']).and(by.label('Back'))).atIndex(0).tap(); + } catch (err) { + return element(by.type('_UIModernBarButton').and(by.label('Back'))).tap(); + } + }, + sleep: ms => new Promise(res => setTimeout(res, ms)) +}; diff --git a/e2e/config.json b/e2e/config.json new file mode 100644 index 0000000000000000000000000000000000000000..4dac170d3611e1aedf2d44b49c675b6ded1b78c1 --- /dev/null +++ b/e2e/config.json @@ -0,0 +1,6 @@ +{ + "setupTestFrameworkScriptFile" : "./init.js", + "testEnvironment": "node", + "bail": true, + "verbose": true +} diff --git a/e2e/init.js b/e2e/init.js new file mode 100644 index 0000000000000000000000000000000000000000..a301c90e6cfc5722bfd9ec167737e2aad2e619ae --- /dev/null +++ b/e2e/init.js @@ -0,0 +1,30 @@ +const detox = require('detox'); +const config = require('../package.json').detox; +const exec = require('shell-utils').exec; +const adapter = require('detox/runners/jest/adapter'); + +jest.setTimeout(300000); +jasmine.getEnv().addReporter(adapter); + +beforeAll(async () => { + await detox.init(config, {launchApp: false}); + disableAndroidEmulatorAnimations(); +}); + +afterAll(async () => { + await adapter.afterAll(); + await detox.cleanup(); +}); + +beforeEach(async () => { + await adapter.beforeEach(); +}); + +function disableAndroidEmulatorAnimations() { + if (device.getPlatform() === 'android') { + const deviceId = device._deviceId; + exec.execAsync(`adb -s ${deviceId} shell settings put global window_animation_scale 0.0`); + exec.execAsync(`adb -s ${deviceId} shell settings put global transition_animation_scale 0.0`); + exec.execAsync(`adb -s ${deviceId} shell settings put global animator_duration_scale 0.0`); + } +} diff --git a/example/android/build.gradle b/example/android/build.gradle index f5195f5cf9d892ae479c61bdaa3e4366b04dbb1c..77dc62dc1aa55ae30c0abfda975267d5a0d89974 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,6 +1,13 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { + ext { + buildToolsVersion = "28.0.3" + minSdkVersion = 16 + compileSdkVersion = 28 + targetSdkVersion = 26 + supportLibVersion = "28.0.3" + } repositories { google() mavenLocal() @@ -8,7 +15,8 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.android.tools.build:gradle:3.3.1' + classpath 'com.google.gms:google-services:4.0.1' } } @@ -21,8 +29,9 @@ allprojects { 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" } + maven { url "$rootDir/../../node_modules/jsc-android/dist" } } } diff --git a/example/android/gradle.properties b/example/android/gradle.properties index 1d3591c8a4c9c29578c36c87f80c05a6aea3ee3f..ccb748f58cadc3512a20b15546b3872e25b71b7b 100644 --- a/example/android/gradle.properties +++ b/example/android/gradle.properties @@ -15,4 +15,7 @@ # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true \ No newline at end of file +# org.gradle.parallel=true + +android.useAndroidX=true +android.enableJetifier=true \ No newline at end of file diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index 77c7721030e23d52b13c86910aa896bb4e0c391d..8cbe8db0c0796a21d70295df834aa17963e4c441 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip diff --git a/example/android/myapplication/.gitignore b/example/android/myapplication/.gitignore index 796b96d1c402326528b4ba3c12ee9d92d0e212e9..65d12b95469b46008614734d430ea36c91c3f1d9 100644 --- a/example/android/myapplication/.gitignore +++ b/example/android/myapplication/.gitignore @@ -1 +1,2 @@ /build +google-services.json \ No newline at end of file diff --git a/example/android/myapplication/build.gradle b/example/android/myapplication/build.gradle index 215a039bd22bcde53266107aa452c56aacfb49fe..cbfe3869dfd15324dd1d69acc7c713d902a11555 100644 --- a/example/android/myapplication/build.gradle +++ b/example/android/myapplication/build.gradle @@ -1,13 +1,22 @@ -apply plugin: 'com.android.application' +apply plugin: "com.android.application" + +project.ext.react = [ + root : "../../../", + entryFile: "index.js", + bundleAssetName: "index.bundle", + bundleInAlpha: true, + bundleInBeta: true +] + +apply from: "../../../node_modules/react-native/react.gradle" android { - compileSdkVersion 26 + compileSdkVersion 28 buildToolsVersion "28.0.3" - defaultConfig { applicationId "com.wix.reactnativenotifications.app" - minSdkVersion 19 - targetSdkVersion 26 + minSdkVersion 16 + targetSdkVersion 28 versionCode 1 versionName "1.0" @@ -21,26 +30,33 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } + compileOptions { + sourceCompatibility 1.8 + targetCompatibility 1.8 + } + packagingOptions { + pickFirst '**/libjsc.so' + pickFirst '**/libc++_shared.so' + } } + configurations.all { - resolutionStrategy.eachDependency { DependencyResolveDetails details -> - def requested = details.requested - if (requested.group == 'com.android.support') { - if (!requested.name.startsWith("multidex")) { - details.useVersion "26.1.0" - } - } + resolutionStrategy { + force 'org.webkit:android-jsc:r236355' } } dependencies { -// compile fileTree(dir: 'libs', include: ['*.jar']) + implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'com.android.support:appcompat-v7:26.1.0' - implementation 'com.android.support:design:26.1.0' + implementation 'com.google.firebase:firebase-core:16.0.0' + implementation 'androidx.appcompat:appcompat:1.0.2' implementation 'com.facebook.react:react-native:+' + implementation 'org.webkit:android-jsc-intl:+' implementation project(':react-native-notifications') testImplementation'junit:junit:4.12' } + +apply plugin: 'com.google.gms.google-services' \ No newline at end of file diff --git a/example/android/myapplication/src/main/AndroidManifest.xml b/example/android/myapplication/src/main/AndroidManifest.xml index 3df23d464dc64562b5bc9578f41bfe28c284a706..12292219a5ae5622c71b75e0060c1326a69d4c5f 100644 --- a/example/android/myapplication/src/main/AndroidManifest.xml +++ b/example/android/myapplication/src/main/AndroidManifest.xml @@ -9,11 +9,9 @@ android:name=".MainApplication" android:allowBackup="false" android:icon="@mipmap/ic_launcher" + android:usesCleartextTraffic="true" android:label="@string/app_name" android:theme="@style/AppTheme"> - = Build.VERSION_CODES.LOLLIPOP) { - layout = (ViewGroup) getLayoutInflater().inflate(R.layout.activity_main, null); - Toolbar toolbar = layout.findViewById(R.id.toolbar); - setActionBar(toolbar); - } else { - layout = (ViewGroup) getLayoutInflater().inflate(R.layout.activity_main_prelollipop, null); - } - mReactRootView = new ReactRootView(this); - layout.addView(mReactRootView); - - setContentView(layout); - - startReactApplication(); - } - - private void startReactApplication() { - mReactRootView.startReactApplication(getReactInstanceManager(), "WixRNNotifications", null); + protected String getMainComponentName() { + return "NotificationsExampleApp"; } } diff --git a/example/android/myapplication/src/main/java/com/wix/reactnativenotifications/app/MainApplication.java b/example/android/myapplication/src/main/java/com/wix/reactnativenotifications/app/MainApplication.java index 87b0d3a6ea174849a221b7e24bab964c1652eb91..021e42a17b0d89c934852449b97e4271b8ef407b 100644 --- a/example/android/myapplication/src/main/java/com/wix/reactnativenotifications/app/MainApplication.java +++ b/example/android/myapplication/src/main/java/com/wix/reactnativenotifications/app/MainApplication.java @@ -6,12 +6,18 @@ import com.facebook.react.ReactApplication; import com.facebook.react.ReactNativeHost; import com.facebook.react.ReactPackage; import com.facebook.react.shell.MainReactPackage; +import com.facebook.soloader.SoLoader; import com.wix.reactnativenotifications.RNNotificationsPackage; import java.util.Arrays; import java.util.List; public class MainApplication extends Application implements ReactApplication { + @Override + public void onCreate() { + super.onCreate(); + SoLoader.init(this, false); + } private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { @Override @@ -24,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"; } }; diff --git a/example/android/myapplication/src/main/res/layout/activity_main.xml b/example/android/myapplication/src/main/res/layout/activity_main.xml deleted file mode 100644 index 3f5177974ac954e0b6350b4bcdaffc4a45e5d62a..0000000000000000000000000000000000000000 --- a/example/android/myapplication/src/main/res/layout/activity_main.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - diff --git a/example/android/myapplication/src/main/res/layout/activity_main_prelollipop.xml b/example/android/myapplication/src/main/res/layout/activity_main_prelollipop.xml deleted file mode 100644 index 75a78a5f3d9a41811d010031a57be679f1603acf..0000000000000000000000000000000000000000 --- a/example/android/myapplication/src/main/res/layout/activity_main_prelollipop.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - diff --git a/example/android/send_notif.py b/example/android/send_notif.py deleted file mode 100755 index 7ac4211bfb696e1200b7e192459a0361618ad737..0000000000000000000000000000000000000000 --- a/example/android/send_notif.py +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/python -from urllib2 import * -import json -import sys - -if len(sys.argv) < 2: - print 'Error: missing token argument' - sys.exit(1) - -API_KEY = 'AIzaSyBVtqdO_SgPVhhXnyNGC_VXSbIX-fxk1YY' -TOKEN = sys.argv[1] -data = { - "to": TOKEN, - "data" : { - "body": "SUCCESS! Sent from script :)", - "title": "Wix Example Project" - } -} -dataJson = json.dumps(data) - -request = Request( - 'https://gcm-http.googleapis.com/gcm/send', - dataJson, - { - "Authorization" : "key="+API_KEY, - "Content-type" : "application/json" - } -) - -print "Sending notification..." -print urlopen(request).read() diff --git a/example/android/settings.gradle b/example/android/settings.gradle index 9ac95524a634aeec524ee31b3c6a27c7f59fa1f9..ac64752eb16eaf7e95e264b658f241d92480a7b9 100644 --- a/example/android/settings.gradle +++ b/example/android/settings.gradle @@ -1,4 +1,4 @@ include ':myapplication' include ':react-native-notifications' -project(':react-native-notifications').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-notifications/android') +project(':react-native-notifications').projectDir = new File(rootProject.projectDir, '../../lib/android/app') \ No newline at end of file diff --git a/example/index.android.js b/example/index.android.js deleted file mode 100644 index af61676b881910c1ab04d39b61ed1f9c70838da9..0000000000000000000000000000000000000000 --- a/example/index.android.js +++ /dev/null @@ -1,158 +0,0 @@ -'use strict'; - -import React, {Component} from 'react'; -import { - AppRegistry, - StyleSheet, - Text, - View, - 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 ( - - Wix React Native Notifications - {this.state.initialNotification ? 'Opened from notification' : ''} - Last notification: {this.state.lastNotification ? '\n'+this.state.lastNotification.body + ` (opened at ''${this.state.notificationRxTime})` : "N/A"} - Time elapsed: {this.state.elapsed} - {"\n\n"} - this.onPostNotification()}> - Try Me! - - this.onCancelNotification()}> - Undo last - - this.onCheckPermissions()}> - Check permissions - - - ) - } - - async onCheckPermissions() { - const hasPermissions = await NotificationsAndroid.isRegisteredForRemoteNotifications(); - if (hasPermissions) { - alert('Yay! You have permissions'); - } else { - alert('Boo! You don\'t have permissions'); - } - } - - 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); diff --git a/example/index.ios.js b/example/index.ios.js deleted file mode 100644 index 5a2d5c3cf348398777109a160d19ecc4e0de0f24..0000000000000000000000000000000000000000 --- a/example/index.ios.js +++ /dev/null @@ -1,157 +0,0 @@ -/** - * Sample React Native App - * https://github.com/facebook/react-native - */ - -import { - AppRegistry, - StyleSheet, - Text, - View -} 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" -}, (action, completed) => { - NotificationsIOS.log("ACTION RECEIVED"); - NotificationsIOS.log(JSON.stringify(action)); - - completed(); -}); - -let replyAction = new NotificationAction({ - activationMode: "background", - title: "Reply", - behavior: "textInput", - authenticationRequired: true, - identifier: "REPLY_ACTION" -}, (action, completed) => { - console.log("ACTION RECEIVED"); - console.log(action); - - completed(); -}); - -let cat = new NotificationCategory({ - identifier: "SOME_CATEGORY", - actions: [upvoteAction, replyAction], - context: "default" -}); - -class NotificationsExampleApp extends Component { - - constructor() { - super(); - NotificationsIOS.addEventListener('remoteNotificationsRegistered', this.onPushRegistered.bind(this)); - NotificationsIOS.requestPermissions([cat]); - - NotificationsIOS.consumeBackgroundQueue(); - - NotificationsIOS.addEventListener('pushKitRegistered', this.onPushKitRegistered.bind(this)); - NotificationsIOS.registerPushKit(); - - NotificationsIOS.addEventListener('notificationReceivedForeground', this.onNotificationReceivedForeground.bind(this)); - NotificationsIOS.addEventListener('notificationReceivedBackground', this.onNotificationReceivedBackground.bind(this)); - NotificationsIOS.addEventListener('notificationOpened', this.onNotificationOpened.bind(this)); - } - - onPushRegistered(deviceToken) { - console.log("Device Token Received: " + deviceToken); - } - - onPushKitRegistered(deviceToken) { - console.log("PushKit Token Received: " + deviceToken); - } - - onNotificationReceivedForeground(notification) { - console.log("Notification Received Foreground: " + JSON.stringify(notification)); - } - - onNotificationReceivedBackground(notification) { - NotificationsIOS.log("Notification Received Background: " + JSON.stringify(notification)); - - let localNotification = NotificationsIOS.localNotification({ - alertBody: "Received background notificiation!", - alertTitle: "Local Notification Title", - alertAction: "Click here to open", - soundName: "chime.aiff", - category: "SOME_CATEGORY", - userInfo: notification.getData() - }); - - // if you want to fire the local notification 10 seconds later, - // add the following line to the notification payload: - // fireDate: new Date(Date.now() + (10 * 1000)).toISOString() - - // NotificationsIOS.backgroundTimeRemaining(time => NotificationsIOS.log("remaining background time: " + time)); - - // NotificationsIOS.cancelLocalNotification(localNotification); - } - - onNotificationOpened(notification) { - console.log("Notification Opened: " + JSON.stringify(notification)); - } - - render() { - return ( - - - Welcome to React Native Notifications Demo App! - - - To get started, edit index.ios.js - - - Press Cmd+R to reload,{'\n'} - Cmd+D or shake for dev menu - - - ); - } - - componentWillUnmount() { - NotificationsIOS.removeEventListener('notificationReceivedForeground', this.onNotificationReceivedForeground.bind(this)); - NotificationsIOS.removeEventListener('notificationReceivedBackground', this.onNotificationReceivedBackground.bind(this)); - NotificationsIOS.removeEventListener('notificationOpened', this.onNotificationOpened.bind(this)); - NotificationsIOS.removeEventListener('remoteNotificationsRegistered', this.onPushRegistered.bind(this)); - NotificationsIOS.removeEventListener('pushKitRegistered', this.onPushKitRegistered.bind(this)); - // NotificationsIOS.resetCategories(); - } - - _onNotification(notification) { - AlertIOS.alert( - 'Notification Received', - 'Alert message: ' + notification.getMessage(), - [{ - text: 'Dismiss', - onPress: null, - }] - ); - } -} - -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); diff --git a/example/index.js b/example/index.js new file mode 100644 index 0000000000000000000000000000000000000000..776b78a9df34eba84239dd273852ff437a3c93db --- /dev/null +++ b/example/index.js @@ -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.data.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 ( + + {`Title: ${notification.title}`} + {`Body: ${notification.body}`} + {`Extra Link Param: ${notification.data.link}`} + + ); + } + + renderOpenedNotification(notification) { + return ( + + {`Title: ${notification.title}`} + {`Body: ${notification.body}`} + {`Notification Clicked: ${notification.data.link}`} + + ); + } + + render() { + const notifications = this.state.notifications.map((notification, idx) => + ( + + {this.renderNotification(notification)} + + )); + const openedNotifications = this.state.openedNotifications.map((notification, idx) => + ( + + {this.renderOpenedNotification(notification)} + + )); + return ( + +