diff --git a/android/app/build.gradle b/android/app/build.gradle
index c750f2845..d74aec64d 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -217,6 +217,10 @@ dependencies {
} else {
implementation jscFlavor
}
+
+ implementation "com.google.code.gson:gson:2.8.5"
+ implementation "com.github.bumptech.glide:glide:4.9.0"
+ annotationProcessor "com.github.bumptech.glide:compiler:4.9.0"
}
// Run this once to be able to run the application with BUCK
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index cba06651e..25f62f1b0 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -47,6 +47,15 @@
+
+
+
> notificationMessages = new HashMap>();
+ public static String KEY_REPLY = "KEY_REPLY";
+ public static String NOTIFICATION_ID = "NOTIFICATION_ID";
+
+ public static void clearMessages(int notId) {
+ notificationMessages.remove(Integer.toString(notId));
+ }
+
+ @Override
+ public void onReceived() throws InvalidNotificationException {
+ final Bundle bundle = mNotificationProps.asBundle();
+
+ String notId = bundle.getString("notId");
+ String message = bundle.getString("message");
+
+ if (notificationMessages.get(notId) == null) {
+ notificationMessages.put(notId, new ArrayList());
+ }
+ notificationMessages.get(notId).add(message);
+
+ super.postNotification(Integer.parseInt(notId));
+
+ notifyReceivedToJS();
+ }
+
+ @Override
+ public void onOpened() {
+ Bundle bundle = mNotificationProps.asBundle();
+ final String notId = bundle.getString("notId");
+ notificationMessages.remove(notId);
+ digestNotification();
}
@Override
protected Notification.Builder getNotificationBuilder(PendingIntent intent) {
- final Resources res = mContext.getResources();
- String packageName = mContext.getPackageName();
+ final Notification.Builder notification = new Notification.Builder(mContext);
Bundle bundle = mNotificationProps.asBundle();
- int smallIconResId = res.getIdentifier("ic_notification", "mipmap", packageName);
- int largeIconResId = res.getIdentifier("ic_launcher", "mipmap", packageName);
String title = bundle.getString("title");
String message = bundle.getString("message");
+ String notId = bundle.getString("notId");
- String CHANNEL_ID = "rocketchatrn_channel_01";
- String CHANNEL_NAME = "All";
-
- final Notification.Builder notification = new Notification.Builder(mContext)
- .setSmallIcon(smallIconResId)
+ notification
.setContentIntent(intent)
.setContentTitle(title)
.setContentText(message)
- .setStyle(new Notification.BigTextStyle().bigText(message))
.setPriority(Notification.PRIORITY_HIGH)
.setDefaults(Notification.DEFAULT_ALL)
.setAutoCancel(true);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- notification.setColor(mContext.getColor(R.color.notification_text));
- }
+ Integer notificationId = Integer.parseInt(notId);
+ notificationChannel(notification);
+ notificationIcons(notification, bundle);
+ notificationStyle(notification, notificationId, bundle);
+ notificationReply(notification, notificationId, bundle);
+ notificationDismiss(notification, notificationId);
+ return notification;
+ }
+
+ private void notifyReceivedToJS() {
+ mJsIOHelper.sendEventToJS(NOTIFICATION_RECEIVED_EVENT_NAME, mNotificationProps.asBundle(), mAppLifecycleFacade.getRunningReactContext());
+ }
+
+ private Bitmap getAvatar(String uri) {
+ try {
+ return Glide.with(mContext)
+ .asBitmap()
+ .apply(RequestOptions.bitmapTransform(new RoundedCorners(10)))
+ .load(uri)
+ .submit(100, 100)
+ .get();
+ } catch (final ExecutionException | InterruptedException e) {
+ return null;
+ }
+ }
+
+ private void notificationIcons(Notification.Builder notification, Bundle bundle) {
+ final Resources res = mContext.getResources();
+ String packageName = mContext.getPackageName();
+
+ int smallIconResId = res.getIdentifier("ic_notification", "mipmap", packageName);
+
+ Gson gson = new Gson();
+ Ejson ejson = gson.fromJson(bundle.getString("ejson", "{}"), Ejson.class);
+
+ notification
+ .setSmallIcon(smallIconResId)
+ .setLargeIcon(getAvatar(ejson.getAvatarUri()));
+ }
+
+ private void notificationChannel(Notification.Builder notification) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ String CHANNEL_ID = "rocketchatrn_channel_01";
+ String CHANNEL_NAME = "All";
+
NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
CHANNEL_NAME,
NotificationManager.IMPORTANCE_DEFAULT);
@@ -63,10 +146,65 @@ public class CustomPushNotification extends PushNotification {
notification.setChannelId(CHANNEL_ID);
}
-
- Bitmap largeIconBitmap = BitmapFactory.decodeResource(res, largeIconResId);
- notification.setLargeIcon(largeIconBitmap);
-
- return notification;
}
+
+ private void notificationStyle(Notification.Builder notification, int notId, Bundle bundle) {
+ Notification.InboxStyle messageStyle = new Notification.InboxStyle();
+ List messages = notificationMessages.get(Integer.toString(notId));
+ if (messages != null) {
+ for (int i = 0; i < messages.size(); i++) {
+ messageStyle.addLine(messages.get(i));
+ }
+ String summary = bundle.getString("summaryText");
+ messageStyle.setSummaryText(summary.replace("%n%", Integer.toString(messages.size())));
+ notification.setNumber(messages.size());
+ }
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ notification.setColor(mContext.getColor(R.color.notification_text));
+ }
+
+ notification.setStyle(messageStyle);
+ }
+
+ private void notificationReply(Notification.Builder notification, int notificationId, Bundle bundle) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
+ return;
+ }
+ String label = "Reply";
+
+ final Resources res = mContext.getResources();
+ String packageName = mContext.getPackageName();
+ int smallIconResId = res.getIdentifier("ic_notification", "mipmap", packageName);
+
+ Intent replyIntent = new Intent(mContext, ReplyBroadcast.class);
+ replyIntent.setAction(KEY_REPLY);
+ replyIntent.putExtra("pushNotification", bundle);
+
+ PendingIntent replyPendingIntent = PendingIntent.getBroadcast(mContext, notificationId, replyIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+
+ RemoteInput remoteInput = new RemoteInput.Builder(KEY_REPLY)
+ .setLabel(label)
+ .build();
+
+ CharSequence title = label;
+ Notification.Action replyAction = new Notification.Action.Builder(smallIconResId, title, replyPendingIntent)
+ .addRemoteInput(remoteInput)
+ .setAllowGeneratedReplies(true)
+ .build();
+
+ notification
+ .setShowWhen(true)
+ .addAction(replyAction);
+ }
+
+ private void notificationDismiss(Notification.Builder notification, int notificationId) {
+ Intent intent = new Intent(mContext, DismissNotification.class);
+ intent.putExtra(NOTIFICATION_ID, notificationId);
+
+ PendingIntent dismissPendingIntent = PendingIntent.getBroadcast(mContext, notificationId, intent, 0);
+
+ notification.setDeleteIntent(dismissPendingIntent);
+ }
+
}
diff --git a/android/app/src/main/java/chat/rocket/reactnative/DismissNotification.java b/android/app/src/main/java/chat/rocket/reactnative/DismissNotification.java
new file mode 100644
index 000000000..32524a35f
--- /dev/null
+++ b/android/app/src/main/java/chat/rocket/reactnative/DismissNotification.java
@@ -0,0 +1,13 @@
+package chat.rocket.reactnative;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+public class DismissNotification extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ int notId = intent.getExtras().getInt(CustomPushNotification.NOTIFICATION_ID);
+ CustomPushNotification.clearMessages(notId);
+ }
+}
diff --git a/android/app/src/main/java/chat/rocket/reactnative/Ejson.java b/android/app/src/main/java/chat/rocket/reactnative/Ejson.java
new file mode 100644
index 000000000..6ef2dfb3d
--- /dev/null
+++ b/android/app/src/main/java/chat/rocket/reactnative/Ejson.java
@@ -0,0 +1,42 @@
+package chat.rocket.reactnative;
+
+import android.content.SharedPreferences;
+
+import chat.rocket.userdefaults.RNUserDefaultsModule;
+
+public class Ejson {
+ String host;
+ String rid;
+ String type;
+ Sender sender;
+
+ private String TOKEN_KEY = "reactnativemeteor_usertoken-";
+ private SharedPreferences sharedPreferences = RNUserDefaultsModule.getPreferences(CustomPushNotification.reactApplicationContext);
+
+ public String getAvatarUri() {
+ if (!type.equals("d")) {
+ return null;
+ }
+ return serverURL() + "/avatar/" + this.sender.username + "?rc_token=" + token() + "&rc_uid=" + userId();
+ }
+
+ public String token() {
+ return sharedPreferences.getString(TOKEN_KEY.concat(userId()), "");
+ }
+
+ public String userId() {
+ return sharedPreferences.getString(TOKEN_KEY.concat(serverURL()), "");
+ }
+
+ public String serverURL() {
+ String url = this.host;
+ if (url.endsWith("/")) {
+ url = url.substring(0, url.length() - 1);
+ }
+ return url;
+ }
+
+ private class Sender {
+ String username;
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/chat/rocket/reactnative/ReplyBroadcast.java b/android/app/src/main/java/chat/rocket/reactnative/ReplyBroadcast.java
new file mode 100644
index 000000000..81c62aeb0
--- /dev/null
+++ b/android/app/src/main/java/chat/rocket/reactnative/ReplyBroadcast.java
@@ -0,0 +1,157 @@
+package chat.rocket.reactnative;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.RemoteInput;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.os.Build;
+import android.os.Bundle;
+import android.util.Log;
+import java.io.IOException;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import java.util.HashMap;
+import java.util.Map;
+
+import okhttp3.Call;
+import okhttp3.MediaType;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+
+import chat.rocket.userdefaults.RNUserDefaultsModule;
+import com.wix.reactnativenotifications.core.NotificationIntentAdapter;
+
+public class ReplyBroadcast extends BroadcastReceiver {
+ private Context mContext;
+ private Bundle bundle;
+ private NotificationManager notificationManager;
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
+ final CharSequence message = getReplyMessage(intent);
+ if (message == null) {
+ return;
+ }
+
+ mContext = context;
+ bundle = NotificationIntentAdapter.extractPendingNotificationDataFromIntent(intent);
+ notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+
+ String notId = bundle.getString("notId");
+
+ Gson gson = new Gson();
+ Ejson ejson = gson.fromJson(bundle.getString("ejson", "{}"), Ejson.class);
+
+ replyToMessage(ejson, Integer.parseInt(notId), message);
+ }
+ }
+
+ protected void replyToMessage(final Ejson ejson, final int notId, final CharSequence message) {
+ String serverURL = ejson.serverURL();
+ String rid = ejson.rid;
+
+ if (serverURL == null || rid == null) {
+ return;
+ }
+
+ final OkHttpClient client = new OkHttpClient();
+ final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
+
+ String json = buildMessage(rid, message.toString());
+
+ CustomPushNotification.clearMessages(notId);
+
+ RequestBody body = RequestBody.create(JSON, json);
+ Request request = new Request.Builder()
+ .header("x-auth-token", ejson.token())
+ .header("x-user-id", ejson.userId())
+ .url(String.format("%s/api/v1/chat.sendMessage", serverURL))
+ .post(body)
+ .build();
+
+ client.newCall(request).enqueue(new okhttp3.Callback() {
+ @Override
+ public void onFailure(Call call, IOException e) {
+ Log.i("RCNotification", String.format("Reply FAILED exception %s", e.getMessage()));
+ onReplyFailed(notificationManager, notId);
+ }
+
+ @Override
+ public void onResponse(Call call, final Response response) throws IOException {
+ if (response.isSuccessful()) {
+ Log.d("RCNotification", "Reply SUCCESS");
+ onReplySuccess(notificationManager, notId);
+ } else {
+ Log.i("RCNotification", String.format("Reply FAILED status %s BODY %s", response.code(), response.body().string()));
+ onReplyFailed(notificationManager, notId);
+ }
+ }
+ });
+ }
+
+ private String getMessageId() {
+ final String ALPHA_NUMERIC_STRING = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+ int count = 17;
+ StringBuilder builder = new StringBuilder();
+ while (count-- != 0) {
+ int character = (int)(Math.random()*ALPHA_NUMERIC_STRING.length());
+ builder.append(ALPHA_NUMERIC_STRING.charAt(character));
+ }
+ return builder.toString();
+ }
+
+ protected String buildMessage(String rid, String message) {
+ Gson gsonBuilder = new GsonBuilder().create();
+
+ Map msgMap = new HashMap();
+ msgMap.put("_id", getMessageId());
+ msgMap.put("rid", rid);
+ msgMap.put("msg", message);
+ msgMap.put("tmid", null);
+
+ Map msg = new HashMap();
+ msg.put("message", msgMap);
+
+ String json = gsonBuilder.toJson(msg);
+
+ return json;
+ }
+
+ protected void onReplyFailed(NotificationManager notificationManager, int notId) {
+ String CHANNEL_ID = "CHANNEL_ID_REPLY_FAILED";
+
+ final Resources res = mContext.getResources();
+ String packageName = mContext.getPackageName();
+ int smallIconResId = res.getIdentifier("ic_notification", "mipmap", packageName);
+
+ NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_ID, NotificationManager.IMPORTANCE_LOW);
+ notificationManager.createNotificationChannel(channel);
+ Notification notification =
+ new Notification.Builder(mContext, CHANNEL_ID)
+ .setContentTitle("Failed to reply message.")
+ .setSmallIcon(smallIconResId)
+ .build();
+
+ notificationManager.notify(notId, notification);
+ }
+
+ protected void onReplySuccess(NotificationManager notificationManager, int notId) {
+ notificationManager.cancel(notId);
+ }
+
+ private CharSequence getReplyMessage(Intent intent) {
+ Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
+ if (remoteInput != null) {
+ return remoteInput.getCharSequence(CustomPushNotification.KEY_REPLY);
+ }
+ return null;
+ }
+}
diff --git a/app/notifications/push/push.ios.js b/app/notifications/push/push.ios.js
index 752eae69c..5207e843d 100644
--- a/app/notifications/push/push.ios.js
+++ b/app/notifications/push/push.ios.js
@@ -1,6 +1,17 @@
-import NotificationsIOS from 'react-native-notifications';
+import NotificationsIOS, { NotificationAction, NotificationCategory } from 'react-native-notifications';
import reduxStore from '../../lib/createStore';
+import I18n from '../../i18n';
+
+const replyAction = new NotificationAction({
+ activationMode: 'background',
+ title: I18n.t('Reply'),
+ textInput: {
+ buttonTitle: I18n.t('Reply'),
+ placeholder: I18n.t('Type_message')
+ },
+ identifier: 'REPLY_ACTION'
+});
class PushNotification {
constructor() {
@@ -20,7 +31,12 @@ class PushNotification {
completion();
});
- NotificationsIOS.requestPermissions();
+ const actions = [];
+ actions.push(new NotificationCategory({
+ identifier: 'MESSAGE',
+ actions: [replyAction]
+ }));
+ NotificationsIOS.requestPermissions(actions);
}
getDeviceToken() {
diff --git a/app/sagas/login.js b/app/sagas/login.js
index 3e0415914..ac2a9b9a1 100644
--- a/app/sagas/login.js
+++ b/app/sagas/login.js
@@ -111,6 +111,7 @@ const handleLoginSuccess = function* handleLoginSuccess({ user }) {
});
yield RNUserDefaults.set(`${ RocketChat.TOKEN_KEY }-${ server }`, user.id);
+ yield RNUserDefaults.set(`${ RocketChat.TOKEN_KEY }-${ user.id }`, user.token);
yield put(setUser(user));
EventEmitter.emit('connected');
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 584c8bd2f..aa95c9b9c 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -381,7 +381,7 @@ PODS:
- React
- RNScreens (2.0.0-alpha.3):
- React
- - RNUserDefaults (1.3.5):
+ - RNUserDefaults (1.7.0):
- React
- RNVectorIcons (6.6.0):
- React
@@ -756,7 +756,7 @@ SPEC CHECKSUMS:
RNReanimated: b2ab0b693dddd2339bd2f300e770f6302d2e960c
RNRootView: 895a4813dedeaca82db2fa868ca1c333d790e494
RNScreens: 402a99b0a27c0c32f079cec12d3ccbd35e20cd7f
- RNUserDefaults: 8a4928443510aa99e4ccb3b53f1bf186593d690b
+ RNUserDefaults: af71a1cdf1c12baf8210bc741c65f5faba9826d6
RNVectorIcons: 0bb4def82230be1333ddaeee9fcba45f0b288ed4
RSKImageCropper: a446db0e8444a036b34f3c43db01b2373baa4b2a
SDWebImage: 96d7f03415ccb28d299d765f93557ff8a617abd8
diff --git a/ios/Pods/Local Podspecs/RNUserDefaults.podspec.json b/ios/Pods/Local Podspecs/RNUserDefaults.podspec.json
index fad4039be..07aed251b 100644
--- a/ios/Pods/Local Podspecs/RNUserDefaults.podspec.json
+++ b/ios/Pods/Local Podspecs/RNUserDefaults.podspec.json
@@ -1,13 +1,13 @@
{
"name": "RNUserDefaults",
- "version": "1.3.5",
- "summary": "Use `UserDefaults` (iOS) with React Native and `AsyncStorage` on AndroidOS.",
- "description": "Use `UserDefaults` (iOS) with React Native and `AsyncStorage` on AndroidOS.",
+ "version": "1.7.0",
+ "summary": "Use `UserDefaults` (iOS) with React Native and `SharedPreferences` on AndroidOS.",
+ "description": "Use `UserDefaults` (iOS) with React Native and `SharedPreferences` on AndroidOS.",
"license": "MIT",
"authors": "djorkaeffalexandre",
- "homepage": "https://github.com/djorkaeffalexandre/rn-user-defaults",
+ "homepage": "https://github.com/RocketChat/rn-user-defaults.git",
"source": {
- "git": "https://github.com/djorkaeffalexandre/rn-user-defaults.git"
+ "git": "https://github.com/RocketChat/rn-user-defaults.git"
},
"requires_arc": true,
"platforms": {
diff --git a/ios/Pods/Manifest.lock b/ios/Pods/Manifest.lock
index 584c8bd2f..aa95c9b9c 100644
--- a/ios/Pods/Manifest.lock
+++ b/ios/Pods/Manifest.lock
@@ -381,7 +381,7 @@ PODS:
- React
- RNScreens (2.0.0-alpha.3):
- React
- - RNUserDefaults (1.3.5):
+ - RNUserDefaults (1.7.0):
- React
- RNVectorIcons (6.6.0):
- React
@@ -756,7 +756,7 @@ SPEC CHECKSUMS:
RNReanimated: b2ab0b693dddd2339bd2f300e770f6302d2e960c
RNRootView: 895a4813dedeaca82db2fa868ca1c333d790e494
RNScreens: 402a99b0a27c0c32f079cec12d3ccbd35e20cd7f
- RNUserDefaults: 8a4928443510aa99e4ccb3b53f1bf186593d690b
+ RNUserDefaults: af71a1cdf1c12baf8210bc741c65f5faba9826d6
RNVectorIcons: 0bb4def82230be1333ddaeee9fcba45f0b288ed4
RSKImageCropper: a446db0e8444a036b34f3c43db01b2373baa4b2a
SDWebImage: 96d7f03415ccb28d299d765f93557ff8a617abd8
diff --git a/ios/RocketChatRN/Info.plist b/ios/RocketChatRN/Info.plist
index 7b31b54b7..9365ee0e8 100644
--- a/ios/RocketChatRN/Info.plist
+++ b/ios/RocketChatRN/Info.plist
@@ -85,6 +85,7 @@
UIBackgroundModes
+ fetch
voip
UILaunchStoryboardName
diff --git a/package.json b/package.json
index 7174582f8..7ebab36e6 100644
--- a/package.json
+++ b/package.json
@@ -95,7 +95,7 @@
"rn-extensions-share": "^2.3.10",
"rn-fetch-blob": "0.11.2",
"rn-root-view": "^1.0.3",
- "rn-user-defaults": "^1.3.5",
+ "rn-user-defaults": "^1.7.0",
"semver": "6.3.0",
"snyk": "1.210.0",
"strip-ansi": "5.2.0",
diff --git a/patches/react-native-notifications+2.0.6.patch b/patches/react-native-notifications+2.0.6.patch
new file mode 100644
index 000000000..b531954b4
--- /dev/null
+++ b/patches/react-native-notifications+2.0.6.patch
@@ -0,0 +1,99 @@
+diff --git a/node_modules/react-native-notifications/RNNotifications/RNNotificationEventHandler.m b/node_modules/react-native-notifications/RNNotifications/RNNotificationEventHandler.m
+index edc4fd4..aeb5eaa 100644
+--- a/node_modules/react-native-notifications/RNNotifications/RNNotificationEventHandler.m
++++ b/node_modules/react-native-notifications/RNNotifications/RNNotificationEventHandler.m
+@@ -28,9 +28,92 @@ - (void)didReceiveForegroundNotification:(UNNotification *)notification withComp
+ [RNEventEmitter sendEvent:RNNotificationReceivedForeground body:[RNNotificationParser parseNotification:notification]];
+ }
+
++/*
++ * Generate a random alphanumeric string to message id
++*/
++-(NSString *)random:(int)len {
++ NSString *letters = @"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
++ NSMutableString *randomString = [NSMutableString stringWithCapacity:len];
++
++ for (int i=0; i 0) {
++ unichar last = [host characterAtIndex:[host length] - 1];
++ if (last == '/') {
++ host = [host substringToIndex:[host length] - 1];
++ }
++ }
++ return host;
++}
++
+ - (void)didReceiveNotificationResponse:(UNNotificationResponse *)response completionHandler:(void (^)(void))completionHandler {
+- [_store setActionCompletionHandler:completionHandler withCompletionKey:response.notification.request.identifier];
+- [RNEventEmitter sendEvent:RNNotificationOpened body:[RNNotificationParser parseNotificationResponse:response]];
++ // if notification response is a REPLY_ACTION
++ if ([response.actionIdentifier isEqualToString:@"REPLY_ACTION"]) {
++ // convert notification data to dictionary payload
++ NSDictionary *notification = [RCTConvert UNNotificationPayload:response.notification];
++
++ // parse ejson from notification
++ NSData *ejsonData = [[notification valueForKey:@"ejson"] dataUsingEncoding:NSUTF8StringEncoding];
++ NSError *error;
++ NSDictionary *ejson = [NSJSONSerialization JSONObjectWithData:ejsonData options:kNilOptions error:&error];
++
++ // data from notification
++ NSString *host = [ejson valueForKey:@"host"];
++ NSString *rid = [ejson valueForKey:@"rid"];
++
++ // msg on textinput of notification
++ NSString *msg = [(UNTextInputNotificationResponse *)response userText];
++
++ // get credentials
++ NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.ios.chat.rocket"];
++ NSString *TOKEN_KEY = @"reactnativemeteor_usertoken";
++ NSString *userId = [userDefaults stringForKey:[NSString stringWithFormat:@"%@-%@", TOKEN_KEY, [self serverURL:host]]];
++ NSString *token = [userDefaults stringForKey:[NSString stringWithFormat:@"%@-%@", TOKEN_KEY, userId]];
++
++ // background task - we need this because fetch doesn't work if app is closed/killed
++ UIApplication *app = [UIApplication sharedApplication];
++ __block UIBackgroundTaskIdentifier task = [app beginBackgroundTaskWithExpirationHandler:^{
++ [app endBackgroundTask:task];
++ task = UIBackgroundTaskInvalid;
++ }];
++ // we use global queue to make requests with app closed/killed
++ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
++ // we make a synchronous request to post new message
++ NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/api/v1/chat.sendMessage", [self serverURL:host]]]];
++
++ NSString *message = [NSString stringWithFormat:@"{ \"message\": { \"_id\": \"%@\", \"msg\": \"%@\", \"rid\": \"%@\" } }", [self random:17], msg, rid];
++
++ [request setHTTPMethod:@"POST"];
++ [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
++ [request addValue:userId forHTTPHeaderField:@"x-user-id"];
++ [request addValue:token forHTTPHeaderField:@"x-auth-token"];
++ [request setHTTPBody:[message dataUsingEncoding:NSUTF8StringEncoding]];
++
++ NSURLResponse *response = nil;
++ NSError *error = nil;
++ NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
++
++ // end background task
++ [app endBackgroundTask:task];
++ task = UIBackgroundTaskInvalid;
++
++ // complete notification response
++ completionHandler();
++ });
++ } else {
++ // We only set initial notification and emit event to JS when not is a reply action
++ [_store setActionCompletionHandler:completionHandler withCompletionKey:response.notification.request.identifier];
++ [RNEventEmitter sendEvent:RNNotificationOpened body:[RNNotificationParser parseNotificationResponse:response]];
++ }
+ }
+
+ @end
\ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
index d951ff215..20d400964 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -10219,10 +10219,10 @@ rn-root-view@^1.0.3:
resolved "https://registry.yarnpkg.com/rn-root-view/-/rn-root-view-1.0.3.tgz#a2cddc717278cb2175fb29b7c006e407b7f0d0e2"
integrity sha512-BIKm8hY5q8+pxK9B5ugYjqutoI9xn2JfxIZKWoaFmAl1bOIM4oXjwFQrRM1e6lFgzz99MN6Mf2dK3Alsywnvvw==
-rn-user-defaults@^1.3.5:
- version "1.3.5"
- resolved "https://registry.yarnpkg.com/rn-user-defaults/-/rn-user-defaults-1.3.5.tgz#8a93325e3fbbc47b1abd4147dc39b25eec8a45ab"
- integrity sha512-mqB57aQBb88QK49revJeJGMQXkPl2qtLeF4mINa7XTVCruAruNkm5wgTKLyS5aNLWdd3XIjkkAUSgH6o6FvIVQ==
+rn-user-defaults@^1.7.0:
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/rn-user-defaults/-/rn-user-defaults-1.7.0.tgz#8d1b79657dec3977e8f8983814b8591821f77236"
+ integrity sha512-Qo6sIH8wldmQ0oOMMvljec4WOa/a1Up1pdatoXZGaPG1gl8OKgKH5HPKyddcABYtxPeBUTPVzCxP/6S6wPCqGQ==
rsvp@^3.3.3:
version "3.6.2"