chat-engine-notifications
v1.1.3
Published
ChatEngine Push Notifications support plugin
Downloads
18
Maintainers
Readme
React Native Push Notification Plugin for ChatEngine
Adds ability to provide remote notification payload for events sent by user to
Chat and manage notifications in
notifications center.
React Native used by plugin to register device for
remote notifications (receive token) and manage remote notifications list in notifications centers
(mostly to mark notification as seen
).
Documentation
Documentation can be found here.
Pre-requirements
- PubNub Function with ChatEngine code.
ChatEngine use PubNub Function as backend and it's configuration can be completed if you follow this url and login with PubNub account credentials.
This page is automated ChatEngine application configurator. After it will complete, you should use keys from newly created ChatEngine application (in PubNub Admin Console). - Complete required steps from this
guide to prepare application for iOS application to use Push Notifications.
Forseen
functionality we also need to enable one of Background Modes fromCapabilities
tab: - Complete required steps from this guide to prepare application for Android application to use FCM service.
Integration
In this document we will use codebase created by react-native-cli
.
React Native toolchain is depends from OS and platform
for this application should be generated. Please see read
this ReactNative Getting Started
page under Building Projects with Native Code - this is required, because plugin use callbacks
from iOS / Android to notify React Native JS code about those
events.
Following command has been used to create application which will be used to demonstrate integration steps:
react-native init ChatEnginePushIntegration
cd ChatEnginePushIntegration
After we created demo application, we need to install required dependencies from yarn by running following command:
yarn add chat-engine chat-engine-notifications
Demo application will require from user tap on Connect
button on all tested devices and on some
devices send application to background, so notifications will appear on screen. There is two buttons
after Connect
which allow to send different event types.
At the end, there is two buttons, which allow to hide last received messages (one-by-on) or hide all
notifications from device's notifications center.
React Native
Open
App.js
and import installed dependencies:import { plugin } from 'chat-engine-notifications'; import ChatEngineCore from 'chat-engine'
Update default imports for react native application in
App.js
:import { TouchableHighlight, Platform, StyleSheet, Alert, Text, View } from 'react-native';
Update default application layout in
App.js
by replacing oof default App component and styles with following code:export default class App extends Component { constructor(properties) { super(properties); this.state = { shouldConnect: true, canSendMessages: false }; } render() { return ( <View style={styles.container}> <TouchableHighlight activeOpacity={1} onPress={this.onPressConnect.bind(this)} style={this.state.shouldConnect ? styles.button : [styles.button, { opacity: 0.3 }]} underlayColor={this.state.shouldConnect ? '#f9efef' : null}> <Text style={styles.buttonTitle}>Connect</Text> </TouchableHighlight> <TouchableHighlight activeOpacity={1} onPress={this.onPressSendLike.bind(this)} style={!this.state.canSendMessages ? styles.button : [styles.button, { opacity: 0.3 }]} underlayColor='#f9efef'> <Text style={styles.buttonTitle}>Send 'like' event</Text> </TouchableHighlight> <TouchableHighlight activeOpacity={1} onPress={this.onPressSendMessage.bind(this)} style={!this.state.canSendMessages ? styles.button : [styles.button, { opacity: 0.3 }]} underlayColor='#f9efef'> <Text style={styles.buttonTitle}>Send message</Text> </TouchableHighlight> <TouchableHighlight activeOpacity={1} onPress={this.onPressSeeLast.bind(this)} style={this.state.canSendMessages ? styles.button : [styles.button, { opacity: 0.3 }]} underlayColor='#f9efef'> <Text style={styles.buttonTitle}>Mark last notification as seen</Text> </TouchableHighlight> <TouchableHighlight activeOpacity={1} onPress={this.onPressSeeAll.bind(this)} style={this.state.canSendMessages ? styles.button : [styles.button, { opacity: 0.3 }]} underlayColor='#f9efef'> <Text style={styles.buttonTitle}>Mark all notifications as seen</Text> </TouchableHighlight> </View> ); } onChatEngineReady() { // Register plugin and subscribe on events from plugin explained in next paragraph. } onPressConnect () { const userName = '<unique user name>'; if (!this.state.shouldConnect) { return; } // Subscribe on '$.ready' event after which plugin will be accessible. ChatEngine.once('$.ready', this.onChatEngineReady.bind(this)); ChatEngine.once('$.error.*', (error) => { // Enable connection button. this.setState({ shouldConnect: true, canSendMessages: false }); Alert.alert( 'Connection error', `ChatEngine connection did fail: ${error.error.message}`, [{ text: 'OK' }], { cancelable: true } ); }); /** * Connect ChatEngine. Make sure to use different 'userName' when running code on another * device. * Or use same name across multiple devices to test 'markAsSeen' functionality. */ ChatEngine.connect(userName, {}, `${userName}-secret`); // Disable connection button. this.setState({ shouldConnect: false }); } onPressSendLike () { if (!this.state.canSendMessages) { return; } ChatEngine.global.emit('like', { text: 'ReactNative Push Notification Plugin' }); } onPressSendMessage () { if (!this.state.canSendMessages) { return; } ChatEngine.global.emit('message', { text: 'This is test message from ReactNative'}); } onPressSeeLast () { let notification = notifications.length ? notifications.pop() : null; if (notification !== null) { ChatEngine.me.notifications.markNotificationAsSeen(notification); } } onPressSeeAll () { notifications = []; ChatEngine.me.notifications.markAllNotificationAsSeen(); } } const styles = StyleSheet.create({ container: { flex: 1, flexDirection: 'column', justifyContent: 'center', alignItems: 'stretch', backgroundColor: '#ffffff', }, button: { borderColor: '#be3532', alignItems: 'center', borderRadius: 5, borderWidth: 1, padding: 10, margin: 5 }, buttonTitle: { color: '#be3532', fontWeight: 'bold' } });
Create ChatEngine by adding following code before
export default class App extends Component
:// Device token storage. let deviceToken = null; // Store received notifications. let notifications = []; // Create ChatEngine. const ChatEngine = ChatEngineCore.create({ publishKey: '<publish-key>', subscribeKey: '<subscribe-key>' });
Make sure to replace
<publish-key>
and<subscribe-key>
keys with actual values for your project from PubNub Admin Console.Add plugin inside of
onChatEngineReady()
function. proto plugin may not work for Android, because it may event some events before application code wil subscribe on them:ChatEngine.me.plugin(plugin({ events: ['$.invite', 'message', 'like'], platforms: { ios: true, android: true }, messageKey: 'text', formatter: (event) => { let payload = null; if (event.event === 'like') { const { chat, sender, data } = event; let title = `${sender} liked your message in ${chat.channel.split('#').pop()}`; let ticker = 'New message like'; let body = data.text; payload = { apns: { aps: { alert: { title, body } } }, gcm: { data: { contentTitle: title, contentText: body, ticker } } }; } else if (event.event === '$.invite') { // Don't send push notifications for invite by providing empty remote notification // payloads ('apns' and 'gcm' not specified). payload = {}; } return payload; } }));
Plugin bundled with default
formatter
for$.invite
andmessage
events. Bundled formatter will be used by default, ifformatter
not specified during configuration. In code snippet we used customformatter
function which decide on what to do basing onevent
name:- use own remote notifications payload for
like
event where notification title will include sender'suuid
and name of chat on which event has been received (extracted fromchat.channel
) - don't send any notifications when inviting remote user (
$.invite
) by returning empty object w/oapns
andgcm
keys. - use bundled formatter for
message
event by simply returningnull
fromformatter
function.
- use own remote notifications payload for
Subscribe on events which is sent by plugin by adding following code at the end of
onChatEngineReady()
function body:ChatEngine.me.notifications.on('$notifications.registered', (token) => { // Store token, because we will need it later to enable push notifications on chat. deviceToken = token; /** * For simplicity, we will enable push notifications on global chat, but it can be any * chat. * * Local user 'direct' chat required if application should be able to mark particular * notifications as seen and hide them from device notification center. */ let chats = [ChatEngine.global, ChatEngine.me.direct]; ChatEngine.me.notifications.enable(chats, deviceToken, (error) => { if (error !== null) { Alert.alert( 'Push Notification error', `Unable to enable notifications for global: ${error.message}`, [{ text: 'OK' }], { cancelable: true } ); } else { // Enable message publish button. this.setState({ canSendMessages: true }); } }); }); ChatEngine.me.notifications.on('$notifications.registration.fail', (error) => { let errorMessage = error.message || error.error.message; Alert.alert( 'Device registration error', `Something went wrong during device registration: ${errorMessage}`, [{ text: 'OK' }], { cancelable: true } ); }); ChatEngine.me.notifications.on('$notifications.received', (notification) => { /** * We received remote notification. Store it for this moment and we cam mark it as seen * later with buttons. */ notifications.push(notification); });
Register notifications channel (required for Android starting from Oreo) by adding following code at the end of
onChatEngineReady()
function body:ChatEngine.me.notifications.registerNotificationChannels([ { id: 'cennotifications', name: 'CENNotifications Channel' } ]);
Note:
cennotifications
is ID, which also used during Android configuration forcom.google.firebase.messaging.default_notification_channel_id
.Request permissions (also trigger device token request) to use notification features (iOS only) adding following code at the end of
onChatEngineReady()
function body:ChatEngine.me.notifications.requestPermissions({ alert: true, badge: false, sound: true }) .then(permissions => console.log('Granted with permissions:', JSON.stringify(permissions))) .catch(error => console.log('Permissions request did fail:', error));
This is last step of plugin integration and preparation, so we can use plugins's functions from this point.
iOS
CocoaPods required for integration completion. Tool can be installed by running following command:
sudo gem install cocoapods
Configure and install project dependencies:
cd ios
Using preferred text editor create
Podfile
with following content:platform :ios, '10.0' target 'ChatEnginePushIntegration' do pod 'React', :path => '../node_modules/react-native', :subspecs => [ 'Core' ] pod 'yoga', :path => '../node_modules/react-native/ReactCommon/yoga' pod 'CENNotifications', :path => '../node_modules/chat-engine-notifications' end
Complete dependencies setup by running following command:
pod install
After commandline tool will complete, make sure to remove certain folders, because of possible conflict between ReactNative packager files and files which has ben copied with CocoaPods:
rm ios/Pods/React/package.json rm -R ios/Pods/React/node_modules
Note: This clean up will be required each time when you call
pod install
.When commandline tool will complete installation process, you should be able to find
ChatEnginePushIntegration.xcworkspace
and open it.If iOS application is expected to run only on iOS 10+, then we can import another API to work with push notifications.
Use combinationShift+Cmd+O
from opened Xcode where type or pasteAppDelegate.h
and hit enter to open this file. Before#import <UIKit/UIKit.h>
paste following code:#import <UserNotifications/UserNotifications.h>
Also, we need to add another protocol from this newly added API to our application delegate like this:
@interface AppDelegate : UIResponder <UNUserNotificationCenterDelegate, UIApplicationDelegate>
Use combination
Shift+Cmd+O
from opened Xcode where type or pasteAppDelegate.h
and hit enter to open this file to add imports of just installed dependency by placing following line before/after#import <React/RCTBundleURLProvider.h>
:#import <CENNotifications/CENNotifications.h>
Now we need to add plugin callbacks to this file:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // ReactNative initialization code. [CENNotifications application:application didFinishLaunchingWithOptions:launchOptions]; // Next required only if you completed step #4. if (@available(iOS 10.0, *)) { UNUserNotificationCenter.currentNotificationCenter.delegate = self; return YES; } - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { [CENNotifications application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; } - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { [CENNotifications application:application didFailToRegisterForRemoteNotificationsWithError:error]; } - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { [CENNotifications application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler]; } - (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo completionHandler:(void (^)())completionHandler { [CENNotifications application:application handleActionWithIdentifier:identifier forRemoteNotification:userInfo completionHandler:completionHandler]; } - (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler { [CENNotifications application:application handleActionWithIdentifier:identifier forRemoteNotification:userInfo withResponseInfo:responseInfo completionHandler:completionHandler]; } - (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler { [CENNotifications userNotificationCenter:center willPresentNotification:notification withCompletionHandler:completionHandler]; } - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler { [CENNotifications userNotificationCenter:center didReceiveNotificationResponse:response withCompletionHandler:completionHandler]; } #pragma mark - Pre-iOS 10 notification delegates - (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings { [CENNotifications application:application didRegisterUserNotificationSettings:notificationSettings]; }
Ensure what proper
Bundle Identifier
andTeam
inSigning
section is set to proper values.
Android
- Open Android project from
android
directory with Android studio. - Use combination
Shift+Cmd+O
from opened Android Studio where type or pastebuild.gradle
and pick from suggestedbuild.gradle (app)
. FindapplicationId
field in it and replace value with application identifier which has been assigned to application which you registered by following this guide. - While in
build.gradle
removeimplementation 'com.google.firebase:firebase-core:16.0.1'
which has been added during Firebase SDK integration (chat-engine-notifications
module integrate newer SDK version). - From project tree root select
ChatEnginePushIntegration
and use combinationCmd+N
to import new module. - From opened dialogue pick
Import Gradle Project
. - With opened file browser navigate to react-native project root and then follow this path and
click
Finish
when done:node_modules/chat-engine-notifications/Libraries/android/chat-engine-notifications
- From project tree root select
ChatEnginePushIntegration
and use combinationCmd+↓
to open module settings. - From opened interface pick
app
fromModules
in sidebar and openDependencies
tab on the right. - Click
+
at the bottom to add new module dependency. - Click on
:chat-engine-notifications
and confirm buttons to leave dependency addition and project structure windows. - Use combination
Shift+Cmd+O
from opened Android Studio where type or pasteMainApplication.java
and hit enter to open it. In opened file findgetPackages()
function and addCENNotificationsPackage
to it:@Override protected List<ReactPackage> getPackages() { return Arrays.<ReactPackage>asList( new MainReactPackage(), new CENNotificationsPackage() ); }
- Use combination
Shift+Cmd+O
from opened Android Studio where type or pasteMainActivity.java
and hit enter to open it. Add or modifyonNewIntent()
:@Override public void onNewIntent(Intent intent) { CENNotifications.onNotification(this, intent, null); }
- Use combination
Shift+Cmd+O
from opened Android Studio where type or pasteAndroidManifest.xml
pick from suggestedAndroidManifest.xml (app/src/main)
. And add permissions and and intention handlers:- Add permissions request after
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
- Add intention handlers right before
<activity android:name="MainActivity"... />
:<receiver android:name="com.pubnub.cennotifications.modules.CENNotificationsBroadcastListener"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="com.pubnub.cennotifications.NOTIFICATION_DELETED" /> </intent-filter> </receiver> <service android:name="com.pubnub.cennotifications.modules.CENNotificationsMessagingService"> <intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT"/> </intent-filter> </service>
- Add Firebase channel information (since Oreo channel is required) before
<receiver android:name="com.pubnub.cennotifications.modules.CENNotificationsBroadcastListener">
:
We user<meta-data android:name="com.google.firebase.messaging.default_notification_channel_id" android:value="cennotifications"/>
cennotifications
for channel name and also use it in example withregisterNotificationChannels()
function call.
- Add permissions request after
Support
- If you need help, have a general question a feature request or to file a bug, contact [email protected]