[MM-16232] ID loaded push notifications (#3562)

* [MM-16232] Android: Fetch notification in notificationReceiptDelivery (#3552)

* Fetch notification in notificationReceiptDelivery

* Fix patch

* Fix patch take 2

* No need to send user_id to ack endpoint

* Just putString in mNotificationProps

* Fix patch take 3

* Revert react-native-notifications patch

* Update patch and fix rejections

* Remove trailing newline in patch

* Move PushNotification changes to end of patch

* npm cache test

* Revert "npm cache test"

This reverts commit d31030aaeeb010c1c3d22a5f6196191eeb849add.

* Created patch after upgrading node

* Created patch after upgrading node take 2

* Remove androidx changes from patch

* Patch packages then jetify

* Cache node_modules without patches

* Remove adding of default message (#3557)

* [MM-16232] iOS: Fetch id-loaded push notification from server (#3556)

* Fetch notification from server

* Parse fetched notification response

* Fix id-loaded notifications for DM/GM's

* audit fix

* Only add keys if they exist

* Throw exception if response code is not 200
This commit is contained in:
Miguel Alatzar 2019-11-18 14:29:49 -08:00 committed by Elias Nahum
parent 84a874918d
commit ba76e5eac7
8 changed files with 134 additions and 37 deletions

View file

@ -91,15 +91,18 @@ commands:
steps:
- restore_cache:
name: Restore npm cache
key: v1-npm-{{ checksum "package.json" }}-{{ arch }}
key: v2-npm-{{ checksum "package.json" }}-{{ arch }}
- run:
name: Getting JavaScript dependencies
command: NODE_ENV=development npm install
command: NODE_ENV=development npm install --ignore-scripts
- save_cache:
name: Save npm cache
key: v1-npm-{{ checksum "package.json" }}-{{ arch }}
key: v2-npm-{{ checksum "package.json" }}-{{ arch }}
paths:
- node_modules
- run:
name: "Run post install scripts"
command: make post-install
pods-dependencies:
description: "Get cocoapods dependencies"

View file

@ -74,6 +74,7 @@ clean: ## Cleans dependencies, previous builds and temp files
@echo Cleanup finished
post-install:
@./node_modules/.bin/patch-package
@./node_modules/.bin/jetify
@rm -f node_modules/intl/.babelrc
@ -84,8 +85,6 @@ post-install:
@sed -i'' -e 's|"./lib/locales": false|"./lib/locales": "./lib/locales"|g' node_modules/intl-relativeformat/package.json
@sed -i'' -e 's|"./locale-data/complete.js": false|"./locale-data/complete.js": "./locale-data/complete.js"|g' node_modules/intl/package.json
@./node_modules/.bin/patch-package
start: | pre-run ## Starts the React Native packager server
$(call start_packager)

View file

@ -20,6 +20,8 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.Build;
import android.provider.Settings.System;
import androidx.annotation.Nullable;
import android.util.Log;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
@ -35,6 +37,9 @@ import com.wix.reactnativenotifications.core.JsIOHelper;
import static com.wix.reactnativenotifications.Defs.NOTIFICATION_RECEIVED_EVENT_NAME;
import com.mattermost.react_native_interface.ResolvePromise;
import com.facebook.react.bridge.WritableMap;
public class CustomPushNotification extends PushNotification {
public static final int MESSAGE_NOTIFICATION_ID = 435345;
public static final String GROUP_KEY_MESSAGES = "mm_group_key_messages";
@ -42,6 +47,8 @@ public class CustomPushNotification extends PushNotification {
public static final String KEY_TEXT_REPLY = "CAN_REPLY";
public static final String NOTIFICATION_REPLIED_EVENT_NAME = "notificationReplied";
private static final String PUSH_TYPE_ID_LOADED = "id_loaded";
private NotificationChannel mHighImportanceChannel;
private NotificationChannel mMinImportanceChannel;
@ -93,16 +100,34 @@ public class CustomPushNotification extends PushNotification {
@Override
public void onReceived() throws InvalidNotificationException {
Bundle data = mNotificationProps.asBundle();
final String channelId = data.getString("channel_id");
final String type = data.getString("type");
final String ackId = data.getString("ack_id");
final Bundle initialData = mNotificationProps.asBundle();
final String type = initialData.getString("type");
final String ackId = initialData.getString("ack_id");
final String postId = initialData.getString("post_id");
final String channelId = initialData.getString("channel_id");
int notificationId = MESSAGE_NOTIFICATION_ID;
if (ackId != null) {
notificationReceiptDelivery(ackId, type);
notificationReceiptDelivery(ackId, postId, type, new ResolvePromise() {
@Override
public void resolve(@Nullable Object value) {
if (PUSH_TYPE_ID_LOADED.equals(type)) {
Bundle response = (Bundle) value;
mNotificationProps = createProps(response);
}
}
@Override
public void reject(String code, String message) {
Log.e("ReactNative", code + ": " + message);
}
});
}
// notificationReceiptDelivery can override mNotificationProps
// so we fetch the bundle again
final Bundle data = mNotificationProps.asBundle();
if (channelId != null) {
notificationId = channelId.hashCode();
Object objCount = channelIdToNotificationCount.get(channelId);
@ -493,8 +518,8 @@ public class CustomPushNotification extends PushNotification {
return message.replaceFirst(senderName, "").replaceFirst(": ", "").trim();
}
private void notificationReceiptDelivery(String ackId, String type) {
ReceiptDelivery.send(context, ackId, type);
private void notificationReceiptDelivery(String ackId, String postId, String type, ResolvePromise promise) {
ReceiptDelivery.send(context, ackId, postId, type, promise);
}
private void createNotificationChannels() {

View file

@ -2,6 +2,7 @@ package com.mattermost.rnbeta;
import android.content.Context;
import androidx.annotation.Nullable;
import android.os.Bundle;
import android.util.Log;
import java.lang.System;
@ -18,13 +19,14 @@ import org.json.JSONException;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.Arguments;
import com.mattermost.react_native_interface.ResolvePromise;
public class ReceiptDelivery {
static final String CURRENT_SERVER_URL = "@currentServerUrl";
public static void send (Context context, final String ackId, final String type) {
public static void send(Context context, final String ackId, final String postId, final String type, ResolvePromise promise) {
final ReactApplicationContext reactApplicationContext = new ReactApplicationContext(context);
MattermostCredentialsHelper.getCredentialsForCurrentServer(reactApplicationContext, new ResolvePromise() {
@ -47,17 +49,22 @@ public class ReceiptDelivery {
}
Log.i("ReactNative", String.format("Send receipt delivery ACK=%s TYPE=%s to URL=%s with TOKEN=%s", ackId, type, serverUrl, token));
execute(serverUrl, token, ackId, type);
execute(serverUrl, postId, token, ackId, type, promise);
}
}
});
}
protected static void execute(String serverUrl, String token, String ackId, String type) {
if (token == null || serverUrl == null) {
protected static void execute(String serverUrl, String postId, String token, String ackId, String type, ResolvePromise promise) {
if (token == null) {
promise.reject("Receipt delivery failure", "Invalid token");
return;
}
if (serverUrl == null) {
promise.reject("Receipt delivery failure", "Invalid server URL");
}
JSONObject json;
long receivedAt = System.currentTimeMillis();
@ -67,8 +74,10 @@ public class ReceiptDelivery {
json.put("received_at", receivedAt);
json.put("platform", "android");
json.put("type", type);
json.put("post_id", postId);
} catch (JSONException e) {
Log.e("ReactNative", "Receipt delivery failed to build json payload");
promise.reject("Receipt delivery failure", e.toString());
return;
}
@ -86,9 +95,24 @@ public class ReceiptDelivery {
.build();
try {
client.newCall(request).execute();
Response response = client.newCall(request).execute();
String responseBody = response.body().toString();
if (response.code() != 200) {
throw new Exception(responseBody);
}
JSONObject jsonResponse = new JSONObject(responseBody);
Bundle bundle = new Bundle();
String keys[] = new String[] {"post_id", "category", "message", "team_id", "channel_id", "channel_name", "type", "sender_id", "sender_name", "version"};
for (int i = 0; i < keys.length; i++) {
String key = keys[i];
if (jsonResponse.has(key)) {
bundle.putString(key, jsonResponse.getString(key));
}
}
promise.resolve(bundle);
} catch (Exception e) {
Log.e("ReactNative", "Receipt delivery failed to send");
promise.reject("Receipt delivery failure", e.toString());
}
}
}

View file

@ -38,7 +38,7 @@ class PushNotification {
this.deviceNotification = {
data,
foreground,
message: data.message,
message: data.body || data.message,
userInfo: data.userInfo,
userInteraction,
};
@ -155,9 +155,10 @@ class PushNotification {
ephemeralStore.appStartedFromPushNotification = true;
}
const data = notification.getData();
const info = {
...notification.getData(),
message: notification.getMessage(),
...data,
message: data.body || notification.getMessage(),
};
if (!userInteraction) {
@ -166,9 +167,10 @@ class PushNotification {
};
onNotificationReceivedForeground = (notification) => {
const data = notification.getData();
const info = {
...notification.getData(),
message: notification.getMessage(),
...data,
message: data.body || notification.getMessage(),
};
this.handleNotification(info, true, false);
};
@ -177,9 +179,10 @@ class PushNotification {
if (action.identifier === REPLY_ACTION) {
this.handleReply(notification, action.text, completion);
} else {
const data = notification.getData();
const info = {
...notification.getData(),
message: notification.getMessage(),
...data,
message: data.body || notification.getMessage(),
};
this.handleNotification(info, false, true);
completion();

View file

@ -9,15 +9,37 @@ class NotificationService: UNNotificationServiceExtension {
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
if let bestAttemptContent = bestAttemptContent {
let ackId = bestAttemptContent.userInfo["ack_id"]
let type = bestAttemptContent.userInfo["type"]
let postId = bestAttemptContent.userInfo["post_id"]
UploadSession.shared.notificationReceipt(
notificationId: bestAttemptContent.userInfo["ack_id"],
notificationId: ackId,
receivedAt: Date().millisencondsSince1970,
type: bestAttemptContent.userInfo["type"]
)
contentHandler(bestAttemptContent)
type: type,
postId: postId
) { data, error in
if (type as? String == "id_loaded") {
guard let data = data, error == nil else {
return
}
let json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as! Dictionary<String,Any>
bestAttemptContent.title = json!["channel_name"] as! String
bestAttemptContent.body = json!["message"] as! String
bestAttemptContent.userInfo["channel_name"] = json!["channel_name"] as! String
bestAttemptContent.userInfo["team_id"] = json!["team_id"] as? String
bestAttemptContent.userInfo["sender_id"] = json!["sender_id"] as! String
bestAttemptContent.userInfo["sender_name"] = json!["sender_name"] as! String
bestAttemptContent.userInfo["root_id"] = json!["root_id"] as? String
bestAttemptContent.userInfo["override_username"] = json!["override_username"] as? String
bestAttemptContent.userInfo["override_icon_url"] = json!["override_icon_url"] as? String
bestAttemptContent.userInfo["from_webhook"] = json!["from_webhook"] as? String
}
contentHandler(bestAttemptContent)
}
}
}

View file

@ -126,8 +126,12 @@ import os.log
}
})
}
public func notificationReceipt(notificationId: Any?, receivedAt: Int, type: Any?) {
notificationReceipt(notificationId:notificationId, receivedAt:receivedAt, type:type, postId:nil, completion:{_, _ in})
}
public func notificationReceipt(notificationId: Any?, receivedAt: Int, type: Any?, postId: Any? = nil, completion: @escaping (Data?, Error?) -> Void) {
if (notificationId != nil) {
let store = StoreManager.shared() as StoreManager
let entities = store.getEntities(true)
@ -142,18 +146,23 @@ import os.log
"id": notificationId as Any,
"received_at": receivedAt,
"platform": "ios",
"type": type as Any
"type": type as Any,
"post_id": postId as Any
]
if !JSONSerialization.isValidJSONObject(jsonObject) {return}
guard let url = URL(string: urlString) else {return}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("Bearer \(sessionToken!)", forHTTPHeaderField: "Authorization")
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
request.httpBody = try? JSONSerialization.data(withJSONObject: jsonObject, options: .prettyPrinted)
URLSession(configuration: .ephemeral).dataTask(with: request).resume()
let task = URLSession(configuration: .ephemeral).dataTask(with: request) { data, _, error in
completion(data, error)
}
task.resume()
}
}
}

View file

@ -165,7 +165,7 @@ index 0d70024..47b962e 100644
PushNotificationProps asProps();
}
diff --git a/node_modules/react-native-notifications/android/app/src/main/java/com/wix/reactnativenotifications/core/notification/PushNotification.java b/node_modules/react-native-notifications/android/app/src/main/java/com/wix/reactnativenotifications/core/notification/PushNotification.java
index 5e4e3d2..871e157 100644
index 5e4e3d2..ec37f87 100644
--- a/node_modules/react-native-notifications/android/app/src/main/java/com/wix/reactnativenotifications/core/notification/PushNotification.java
+++ b/node_modules/react-native-notifications/android/app/src/main/java/com/wix/reactnativenotifications/core/notification/PushNotification.java
@@ -1,5 +1,6 @@
@ -175,16 +175,28 @@ index 5e4e3d2..871e157 100644
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
@@ -20,7 +21,9 @@ import com.wix.reactnativenotifications.core.InitialNotificationHolder;
@@ -20,18 +21,20 @@ import com.wix.reactnativenotifications.core.InitialNotificationHolder;
import com.wix.reactnativenotifications.core.JsIOHelper;
import com.wix.reactnativenotifications.core.NotificationIntentAdapter;
import com.wix.reactnativenotifications.core.ProxyService;
+import com.wix.reactnativenotifications.core.helpers.ScheduleNotificationHelper;
+import static com.wix.reactnativenotifications.Defs.LOGTAG;
import static com.wix.reactnativenotifications.Defs.NOTIFICATION_OPENED_EVENT_NAME;
import static com.wix.reactnativenotifications.Defs.NOTIFICATION_RECEIVED_EVENT_NAME;
import static com.wix.reactnativenotifications.Defs.NOTIFICATION_RECEIVED_FOREGROUND_EVENT_NAME;
public class PushNotification implements IPushNotification {
+ protected PushNotificationProps mNotificationProps;
final protected Context mContext;
final protected AppLifecycleFacade mAppLifecycleFacade;
final protected AppLaunchHelper mAppLaunchHelper;
final protected JsIOHelper mJsIOHelper;
- final protected PushNotificationProps mNotificationProps;
final protected AppVisibilityListener mAppVisibilityListener = new AppVisibilityListener() {
@Override
public void onAppVisible() {
@@ -80,6 +83,41 @@ public class PushNotification implements IPushNotification {
return postNotification(notificationId);
}