[NEW] Reply notification (#1448)

This commit is contained in:
Djorkaeff Alexandre 2019-12-17 13:27:13 -03:00 committed by Diego Mello
parent 9084f22ab1
commit 5f0389c7de
15 changed files with 518 additions and 38 deletions

View File

@ -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

View File

@ -47,6 +47,15 @@
</intent-filter>
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
<receiver
android:name=".ReplyBroadcast"
android:enabled="true"
android:exported="false" />
<receiver
android:name=".DismissNotification"
android:enabled="true"
android:exported="false" >
</receiver>
<activity
android:noHistory="true"
android:name=".share.ShareActivity"

View File

@ -4,56 +4,139 @@ import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.RemoteInput;
import android.content.Intent;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings.System;
import android.media.RingtoneManager;
import com.google.gson.*;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.bumptech.glide.request.RequestOptions;
import java.util.concurrent.ExecutionException;
import java.lang.InterruptedException;
import com.facebook.react.bridge.ReactApplicationContext;
import com.wix.reactnativenotifications.core.AppLaunchHelper;
import com.wix.reactnativenotifications.core.AppLifecycleFacade;
import com.wix.reactnativenotifications.core.JsIOHelper;
import com.wix.reactnativenotifications.core.notification.PushNotification;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ArrayList;
import static com.wix.reactnativenotifications.Defs.NOTIFICATION_RECEIVED_EVENT_NAME;
public class CustomPushNotification extends PushNotification {
public static ReactApplicationContext reactApplicationContext;
public CustomPushNotification(Context context, Bundle bundle, AppLifecycleFacade appLifecycleFacade, AppLaunchHelper appLaunchHelper, JsIOHelper jsIoHelper) {
super(context, bundle, appLifecycleFacade, appLaunchHelper, jsIoHelper);
reactApplicationContext = new ReactApplicationContext(context);
}
private static Map<String, List<String>> notificationMessages = new HashMap<String, List<String>>();
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<String>());
}
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<String> 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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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() {

View File

@ -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');

View File

@ -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

View File

@ -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": {

View File

@ -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

View File

@ -85,6 +85,7 @@
</dict>
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>voip</string>
</array>
<key>UILaunchStoryboardName</key>

View File

@ -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",

View File

@ -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<len; i++) {
+ [randomString appendFormat: @"%C", [letters characterAtIndex: arc4random_uniform([letters length])]];
+ }
+
+ return randomString;
+}
+
+/*
+ * Remove trailing slash on server url from notification
+*/
+-(NSString *)serverURL:(NSString *)host {
+ if ([host length] > 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

View File

@ -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"