[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:
parent
84a874918d
commit
ba76e5eac7
8 changed files with 134 additions and 37 deletions
|
|
@ -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"
|
||||
|
|
|
|||
3
Makefile
3
Makefile
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue