From b68b0b84dfddbd90430c262ad25aec9f5a162d21 Mon Sep 17 00:00:00 2001 From: Djorkaeff Alexandre Date: Tue, 28 Jul 2020 10:43:43 -0300 Subject: [PATCH] [NEW] Push notification data privacy (#2213) * [WIP] Notification Service * [WIP] Android push notification privacy * [WIP] Retry request when it fails (iOS) * [WIP] Override notification bundle * [CHORE] Remove unnecessary import * [WIP] Check notification Type (iOS) * [WIP] Change to notification endpoint * eof * fix unwrap conditional value * turn run request synchronous * fix bundle info * eof * remove extra tab * undo unnecessary change * remove not working code for a while * fix notification title * change endpoint and received/sent data * message-id-only working properly on android * notification privacy working on ios * invalidate circleCI yarn cache * Fix provisioning profiles * fix notification service version * fix unwrap nil * compatibility older servers android * show received notification when cant fetch content from server * undo some android changes * prevent group & reply fallback notifications * dont show more than one fallback notification by server Co-authored-by: Diego Mello --- .../chat/rocket/reactnative/Callback.java | 10 ++ .../reactnative/CustomPushNotification.java | 75 +++++++-- .../java/chat/rocket/reactnative/Ejson.java | 2 + .../rocket/reactnative/LoadNotification.java | 103 ++++++++++++ ios/NotificationService/Info.plist | 33 ++++ .../NotificationService.entitlements | 10 ++ .../NotificationService.swift | 154 ++++++++++++++++++ ios/RocketChatRN.xcodeproj/project.pbxproj | 149 +++++++++++++++++ .../xcschemes/NotificationService.xcscheme | 100 ++++++++++++ ios/fastlane/Fastfile | 1 + package.json | 2 +- .../react-native-notifications+2.1.7.patch | 13 ++ yarn.lock | 2 +- 13 files changed, 636 insertions(+), 18 deletions(-) create mode 100644 android/app/src/main/java/chat/rocket/reactnative/Callback.java create mode 100644 android/app/src/main/java/chat/rocket/reactnative/LoadNotification.java create mode 100644 ios/NotificationService/Info.plist create mode 100644 ios/NotificationService/NotificationService.entitlements create mode 100644 ios/NotificationService/NotificationService.swift create mode 100644 ios/RocketChatRN.xcodeproj/xcshareddata/xcschemes/NotificationService.xcscheme diff --git a/android/app/src/main/java/chat/rocket/reactnative/Callback.java b/android/app/src/main/java/chat/rocket/reactnative/Callback.java new file mode 100644 index 00000000..2c2b2833 --- /dev/null +++ b/android/app/src/main/java/chat/rocket/reactnative/Callback.java @@ -0,0 +1,10 @@ +package chat.rocket.reactnative; + +import android.os.Bundle; +import androidx.annotation.Nullable; + +public class Callback { + public void call(@Nullable Bundle bundle) { + + } +} diff --git a/android/app/src/main/java/chat/rocket/reactnative/CustomPushNotification.java b/android/app/src/main/java/chat/rocket/reactnative/CustomPushNotification.java index 17002faa..401314d7 100644 --- a/android/app/src/main/java/chat/rocket/reactnative/CustomPushNotification.java +++ b/android/app/src/main/java/chat/rocket/reactnative/CustomPushNotification.java @@ -14,8 +14,9 @@ import android.graphics.drawable.Icon; import android.os.Build; import android.os.Bundle; import android.app.Person; +import androidx.annotation.Nullable; -import com.google.gson.*; +import com.google.gson.Gson; import com.bumptech.glide.Glide; import com.bumptech.glide.load.resource.bitmap.RoundedCorners; import com.bumptech.glide.request.RequestOptions; @@ -33,15 +34,18 @@ import java.util.List; import java.util.Map; import java.util.ArrayList; import java.util.Date; +import java.util.Iterator; import static com.wix.reactnativenotifications.Defs.NOTIFICATION_RECEIVED_EVENT_NAME; public class CustomPushNotification extends PushNotification { public static ReactApplicationContext reactApplicationContext; + final NotificationManager notificationManager; public CustomPushNotification(Context context, Bundle bundle, AppLifecycleFacade appLifecycleFacade, AppLaunchHelper appLaunchHelper, JsIOHelper jsIoHelper) { super(context, bundle, appLifecycleFacade, appLaunchHelper, jsIoHelper); reactApplicationContext = new ReactApplicationContext(context); + notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); } private static Map> notificationMessages = new HashMap>(); @@ -54,29 +58,39 @@ public class CustomPushNotification extends PushNotification { @Override public void onReceived() throws InvalidNotificationException { - final Bundle bundle = mNotificationProps.asBundle(); + Bundle received = mNotificationProps.asBundle(); + Ejson receivedEjson = new Gson().fromJson(received.getString("ejson", "{}"), Ejson.class); + if (receivedEjson.notificationType != null && receivedEjson.notificationType.equals("message-id-only")) { + notificationLoad(receivedEjson.serverURL(), receivedEjson.messageId, new Callback() { + @Override + public void call(@Nullable Bundle bundle) { + if (bundle != null) { + mNotificationProps = createProps(bundle); + } + } + }); + } + + // We should re-read these values since that can be changed by notificationLoad + Bundle bundle = mNotificationProps.asBundle(); + Ejson loadedEjson = new Gson().fromJson(bundle.getString("ejson", "{}"), Ejson.class); String notId = bundle.getString("notId", "1"); - String title = bundle.getString("title"); if (notificationMessages.get(notId) == null) { notificationMessages.put(notId, new ArrayList()); } - Gson gson = new Gson(); - Ejson ejson = gson.fromJson(bundle.getString("ejson", "{}"), Ejson.class); - - boolean hasSender = ejson.sender != null; + boolean hasSender = loadedEjson.sender != null; + String title = bundle.getString("title"); bundle.putLong("time", new Date().getTime()); - bundle.putString("username", hasSender ? ejson.sender.username : title); - bundle.putString("senderId", hasSender ? ejson.sender._id : "1"); - bundle.putString("avatarUri", ejson.getAvatarUri()); + bundle.putString("username", hasSender ? loadedEjson.sender.username : title); + bundle.putString("senderId", hasSender ? loadedEjson.sender._id : "1"); + bundle.putString("avatarUri", loadedEjson.getAvatarUri()); notificationMessages.get(notId).add(bundle); - - super.postNotification(Integer.parseInt(notId)); - + postNotification(Integer.parseInt(notId)); notifyReceivedToJS(); } @@ -96,9 +110,11 @@ public class CustomPushNotification extends PushNotification { String notId = bundle.getString("notId", "1"); String title = bundle.getString("title"); String message = bundle.getString("message"); + Boolean notificationLoaded = bundle.getBoolean("notificationLoaded", false); + Ejson ejson = new Gson().fromJson(bundle.getString("ejson", "{}"), Ejson.class); notification - .setContentTitle(title) + .setContentTitle(title) .setContentText(message) .setContentIntent(intent) .setPriority(Notification.PRIORITY_HIGH) @@ -109,10 +125,34 @@ public class CustomPushNotification extends PushNotification { notificationColor(notification); notificationChannel(notification); notificationIcons(notification, bundle); - notificationStyle(notification, notificationId, bundle); - notificationReply(notification, notificationId, bundle); notificationDismiss(notification, notificationId); + // if notificationType is null (RC < 3.5) or notificationType is different of message-id-only or notification was loaded successfully + if (ejson.notificationType == null || !ejson.notificationType.equals("message-id-only") || notificationLoaded) { + notificationStyle(notification, notificationId, bundle); + notificationReply(notification, notificationId, bundle); + + // message couldn't be loaded from server (Fallback notification) + } else { + Gson gson = new Gson(); + // iterate over the current notification ids to dismiss fallback notifications from same server + for (Map.Entry> bundleList : notificationMessages.entrySet()) { + // iterate over the notifications with this id (same host + rid) + Iterator iterator = bundleList.getValue().iterator(); + while (iterator.hasNext()) { + Bundle not = (Bundle) iterator.next(); + // get the notification info + Ejson notEjson = gson.fromJson(not.getString("ejson", "{}"), Ejson.class); + // if already has a notification from same server + if (ejson.serverURL().equals(notEjson.serverURL())) { + String id = not.getString("notId"); + // cancel this notification + notificationManager.cancel(Integer.parseInt(id)); + } + } + } + } + return notification; } @@ -300,4 +340,7 @@ public class CustomPushNotification extends PushNotification { notification.setDeleteIntent(dismissPendingIntent); } + private void notificationLoad(String server, String messageId, Callback callback) { + LoadNotification.load(reactApplicationContext, server, messageId, callback); + } } diff --git a/android/app/src/main/java/chat/rocket/reactnative/Ejson.java b/android/app/src/main/java/chat/rocket/reactnative/Ejson.java index 48c37037..0c68e475 100644 --- a/android/app/src/main/java/chat/rocket/reactnative/Ejson.java +++ b/android/app/src/main/java/chat/rocket/reactnative/Ejson.java @@ -9,6 +9,8 @@ public class Ejson { String rid; String type; Sender sender; + String messageId; + String notificationType; private String TOKEN_KEY = "reactnativemeteor_usertoken-"; private SharedPreferences sharedPreferences = RNUserDefaultsModule.getPreferences(CustomPushNotification.reactApplicationContext); diff --git a/android/app/src/main/java/chat/rocket/reactnative/LoadNotification.java b/android/app/src/main/java/chat/rocket/reactnative/LoadNotification.java new file mode 100644 index 00000000..b80dfb7f --- /dev/null +++ b/android/app/src/main/java/chat/rocket/reactnative/LoadNotification.java @@ -0,0 +1,103 @@ +package chat.rocket.reactnative; + +import android.os.Bundle; +import android.content.Context; +import android.content.SharedPreferences; + +import okhttp3.Call; +import okhttp3.OkHttpClient; +import okhttp3.HttpUrl; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.Interceptor; + +import com.google.gson.Gson; +import java.io.IOException; + +import com.facebook.react.bridge.ReactApplicationContext; + +import chat.rocket.userdefaults.RNUserDefaultsModule; + +class JsonResponse { + Data data; + + class Data { + Notification notification; + + class Notification { + String notId; + String title; + String text; + Payload payload; + + class Payload { + String host; + String rid; + String type; + Sender sender; + String messageId; + String notificationType; + + class Sender { + String username; + String _id; + } + } + } + } +} + +public class LoadNotification { + private static int RETRY_COUNT = 0; + private static int[] TIMEOUT = new int[]{ 0, 1, 3, 5, 10 }; + private static String TOKEN_KEY = "reactnativemeteor_usertoken-"; + private static SharedPreferences sharedPreferences = RNUserDefaultsModule.getPreferences(CustomPushNotification.reactApplicationContext); + + public static void load(ReactApplicationContext reactApplicationContext, final String host, final String msgId, Callback callback) { + final OkHttpClient client = new OkHttpClient(); + HttpUrl.Builder url = HttpUrl.parse(host.concat("/api/v1/push.get")).newBuilder(); + + String userId = sharedPreferences.getString(TOKEN_KEY.concat(host), ""); + String token = sharedPreferences.getString(TOKEN_KEY.concat(userId), ""); + + Request request = new Request.Builder() + .header("x-user-id", userId) + .header("x-auth-token", token) + .url(url.addQueryParameter("id", msgId).build()) + .build(); + + runRequest(client, request, callback); + } + + private static void runRequest(OkHttpClient client, Request request, Callback callback) { + try { + Thread.sleep(TIMEOUT[RETRY_COUNT] * 1000); + + Response response = client.newCall(request).execute(); + String body = response.body().string(); + if (!response.isSuccessful()) { + throw new Exception("Error"); + } + + Gson gson = new Gson(); + JsonResponse json = gson.fromJson(body, JsonResponse.class); + + Bundle bundle = new Bundle(); + bundle.putString("notId", json.data.notification.notId); + bundle.putString("title", json.data.notification.title); + bundle.putString("message", json.data.notification.text); + bundle.putString("ejson", gson.toJson(json.data.notification.payload)); + bundle.putBoolean("notificationLoaded", true); + + callback.call(bundle); + + } catch (Exception e) { + if (RETRY_COUNT <= TIMEOUT.length) { + RETRY_COUNT++; + runRequest(client, request, callback); + } else { + callback.call(null); + } + } + } +} diff --git a/ios/NotificationService/Info.plist b/ios/NotificationService/Info.plist new file mode 100644 index 00000000..496dfc6c --- /dev/null +++ b/ios/NotificationService/Info.plist @@ -0,0 +1,33 @@ + + + + + AppGroup + group.ios.chat.rocket + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + NotificationService + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleVersion + 1 + NSExtension + + NSExtensionPointIdentifier + com.apple.usernotifications.service + NSExtensionPrincipalClass + $(PRODUCT_MODULE_NAME).NotificationService + + + diff --git a/ios/NotificationService/NotificationService.entitlements b/ios/NotificationService/NotificationService.entitlements new file mode 100644 index 00000000..f48f06fb --- /dev/null +++ b/ios/NotificationService/NotificationService.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.ios.chat.rocket + + + diff --git a/ios/NotificationService/NotificationService.swift b/ios/NotificationService/NotificationService.swift new file mode 100644 index 00000000..28db4ec6 --- /dev/null +++ b/ios/NotificationService/NotificationService.swift @@ -0,0 +1,154 @@ +import CoreLocation +import UserNotifications + +struct PushResponse: Decodable { + let success: Bool + let data: Data + + struct Data: Decodable { + let notification: Notification + + struct Notification: Decodable { + let notId: Int + let title: String + let text: String + let payload: Payload + + struct Payload: Decodable, Encodable { + let host: String + let rid: String? + let type: String? + let sender: Sender? + let messageId: String + let notificationType: String? + let name: String? + + struct Sender: Decodable, Encodable { + let _id: String + let username: String + let name: String + } + } + } + } +} + +class NotificationService: UNNotificationServiceExtension { + + var contentHandler: ((UNNotificationContent) -> Void)? + var bestAttemptContent: UNMutableNotificationContent? + + var retryCount = 0 + var retryTimeout = [1.0, 3.0, 5.0, 10.0] + + override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { + self.contentHandler = contentHandler + bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) + + if let bestAttemptContent = bestAttemptContent { + let ejson = (bestAttemptContent.userInfo["ejson"] as? String ?? "").data(using: .utf8)! + guard let data = try? (JSONDecoder().decode(PushResponse.Data.Notification.Payload.self, from: ejson)) else { + return + } + + let notificationType = data.notificationType ?? "" + + // If the notification have the content at her payload, show it + if notificationType != "message-id-only" { + contentHandler(bestAttemptContent) + return + } + + let suiteName = Bundle.main.object(forInfoDictionaryKey: "AppGroup") as! String + let userDefaults = UserDefaults(suiteName: suiteName) + + var server = data.host + if (server.last == "/") { + server.removeLast() + } + let msgId = data.messageId + + let userId = userDefaults?.string(forKey: "reactnativemeteor_usertoken-\(server)") ?? "" + let token = userDefaults?.string(forKey: "reactnativemeteor_usertoken-\(userId)") ?? "" + + if userId.isEmpty || token.isEmpty { + contentHandler(bestAttemptContent) + return + } + + var urlComponents = URLComponents(string: "\(server)/api/v1/push.get")! + let queryItems = [URLQueryItem(name: "id", value: msgId)] + urlComponents.queryItems = queryItems + + var request = URLRequest(url: urlComponents.url!) + request.httpMethod = "GET" + request.addValue(userId, forHTTPHeaderField: "x-user-id") + request.addValue(token, forHTTPHeaderField: "x-auth-token") + + runRequest(request: request, bestAttemptContent: bestAttemptContent, contentHandler: contentHandler) + } + } + + func runRequest(request: URLRequest, bestAttemptContent: UNMutableNotificationContent, contentHandler: @escaping (UNNotificationContent) -> Void) { + let task = URLSession.shared.dataTask(with: request) {(data, response, error) in + + func retryRequest() { + // if we can try again + if self.retryCount < self.retryTimeout.count { + // Try again after X seconds + DispatchQueue.main.asyncAfter(deadline: .now() + self.retryTimeout[self.retryCount], execute: { + self.runRequest(request: request, bestAttemptContent: bestAttemptContent, contentHandler: contentHandler) + self.retryCount += 1 + }) + } + } + + // If some error happened + if error != nil { + retryRequest() + + // Check if the request did successfully + } else if let response = response as? HTTPURLResponse { + // if it not was successfully + if response.statusCode != 200 { + retryRequest() + + // If the response status is 200 + } else { + // Process data + if let data = data { + // Parse data of response + let push = try? (JSONDecoder().decode(PushResponse.self, from: data)) + if let push = push { + if push.success { + bestAttemptContent.title = push.data.notification.title + bestAttemptContent.body = push.data.notification.text + + let payload = try? (JSONEncoder().encode(push.data.notification.payload)) + if let payload = payload { + bestAttemptContent.userInfo["ejson"] = String(data: payload, encoding: .utf8) ?? "{}" + } + + // Show notification with the content modified + contentHandler(bestAttemptContent) + return + } + } + } + retryRequest() + } + } + } + + task.resume() + } + + override func serviceExtensionTimeWillExpire() { + // Called just before the extension will be terminated by the system. + // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used. + if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent { + contentHandler(bestAttemptContent) + } + } + +} diff --git a/ios/RocketChatRN.xcodeproj/project.pbxproj b/ios/RocketChatRN.xcodeproj/project.pbxproj index 208a331e..b19ec75f 100644 --- a/ios/RocketChatRN.xcodeproj/project.pbxproj +++ b/ios/RocketChatRN.xcodeproj/project.pbxproj @@ -28,6 +28,8 @@ 1EC6ACF622CBA01500A41C61 /* ShareRocketChatRN.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC6ACF522CBA01500A41C61 /* ShareRocketChatRN.m */; }; 1ED59D4C22CBA77D00C54289 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 1ED59D4B22CBA77D00C54289 /* GoogleService-Info.plist */; }; 1EDDE57A22DFAD8E0078F69D /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1EDDE57922DFAD8E0078F69D /* Images.xcassets */; }; + 1EFEB5982493B6640072EDC0 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EFEB5972493B6640072EDC0 /* NotificationService.swift */; }; + 1EFEB59C2493B6640072EDC0 /* NotificationService.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 1EFEB5952493B6640072EDC0 /* NotificationService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 24A2AEF2383D44B586D31C01 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 06BB44DD4855498082A744AD /* libz.tbd */; }; 50046CB6BDA69B9232CF66D9 /* libPods-RocketChatRN.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C235DC7B31A4D1578EDEF219 /* libPods-RocketChatRN.a */; }; 7A006F14229C83B600803143 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7A006F13229C83B600803143 /* GoogleService-Info.plist */; }; @@ -48,6 +50,13 @@ remoteGlobalIDString = 1EC6ACAF22CB9FC300A41C61; remoteInfo = ShareRocketChatRN; }; + 1EFEB59A2493B6640072EDC0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 1EFEB5942493B6640072EDC0; + remoteInfo = NotificationService; + }; 7AAA749A23043AD300F1ADE9 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 290E43E48AD8418287FA99D6 /* WatermelonDB.xcodeproj */; @@ -65,6 +74,7 @@ dstSubfolderSpec = 13; files = ( 1EC6ACBB22CB9FC300A41C61 /* ShareRocketChatRN.appex in Embed App Extensions */, + 1EFEB59C2493B6640072EDC0 /* NotificationService.appex in Embed App Extensions */, ); name = "Embed App Extensions"; runOnlyForDeploymentPostprocessing = 0; @@ -98,6 +108,10 @@ 1EC6AD6022CBA20C00A41C61 /* ShareRocketChatRN.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ShareRocketChatRN.entitlements; sourceTree = ""; }; 1ED59D4B22CBA77D00C54289 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = SOURCE_ROOT; }; 1EDDE57922DFAD8E0078F69D /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + 1EFEB5952493B6640072EDC0 /* NotificationService.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NotificationService.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 1EFEB5972493B6640072EDC0 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; }; + 1EFEB5992493B6640072EDC0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 1EFEB5A12493B67D0072EDC0 /* NotificationService.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NotificationService.entitlements; sourceTree = ""; }; 290E43E48AD8418287FA99D6 /* WatermelonDB.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = WatermelonDB.xcodeproj; path = "../node_modules/@nozbe/watermelondb/native/ios/WatermelonDB.xcodeproj"; sourceTree = ""; }; 60B2A6A31FC4588700BD58E5 /* RocketChatRN.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = RocketChatRN.entitlements; path = RocketChatRN/RocketChatRN.entitlements; sourceTree = ""; }; 66D6B1D0567051BE541450C9 /* Pods-RocketChatRN.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RocketChatRN.release.xcconfig"; path = "Pods/Target Support Files/Pods-RocketChatRN/Pods-RocketChatRN.release.xcconfig"; sourceTree = ""; }; @@ -146,6 +160,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 1EFEB5922493B6640072EDC0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -180,6 +201,16 @@ path = ShareRocketChatRN; sourceTree = ""; }; + 1EFEB5962493B6640072EDC0 /* NotificationService */ = { + isa = PBXGroup; + children = ( + 1EFEB5A12493B67D0072EDC0 /* NotificationService.entitlements */, + 1EFEB5972493B6640072EDC0 /* NotificationService.swift */, + 1EFEB5992493B6640072EDC0 /* Info.plist */, + ); + path = NotificationService; + sourceTree = ""; + }; 22CA7F59107E0C79C2506C7C /* Pods */ = { isa = PBXGroup; children = ( @@ -213,6 +244,7 @@ 13B07FAE1A68108700A75B9A /* RocketChatRN */, 832341AE1AAA6A7D00B99B32 /* Libraries */, 1EC6ACB122CB9FC300A41C61 /* ShareRocketChatRN */, + 1EFEB5962493B6640072EDC0 /* NotificationService */, 83CBBA001A601CBA00E9B192 /* Products */, BB4B591B5FC44CD9986DB2A6 /* Frameworks */, AF5E16F0398347E6A80C8CBE /* Resources */, @@ -228,6 +260,7 @@ children = ( 13B07F961A680F5B00A75B9A /* RocketChatRN.app */, 1EC6ACB022CB9FC300A41C61 /* ShareRocketChatRN.appex */, + 1EFEB5952493B6640072EDC0 /* NotificationService.appex */, ); name = Products; sourceTree = ""; @@ -293,6 +326,7 @@ ); dependencies = ( 1EC6ACBA22CB9FC300A41C61 /* PBXTargetDependency */, + 1EFEB59B2493B6640072EDC0 /* PBXTargetDependency */, ); name = RocketChatRN; productName = "Hello World"; @@ -321,6 +355,23 @@ productReference = 1EC6ACB022CB9FC300A41C61 /* ShareRocketChatRN.appex */; productType = "com.apple.product-type.app-extension"; }; + 1EFEB5942493B6640072EDC0 /* NotificationService */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1EFEB5A02493B6640072EDC0 /* Build configuration list for PBXNativeTarget "NotificationService" */; + buildPhases = ( + 1EFEB5912493B6640072EDC0 /* Sources */, + 1EFEB5922493B6640072EDC0 /* Frameworks */, + 1EFEB5932493B6640072EDC0 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = NotificationService; + productName = NotificationService; + productReference = 1EFEB5952493B6640072EDC0 /* NotificationService.appex */; + productType = "com.apple.product-type.app-extension"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -328,6 +379,7 @@ isa = PBXProject; attributes = { DefaultBuildSystemTypeForWorkspace = Original; + LastSwiftUpdateCheck = 1150; LastUpgradeCheck = 1130; ORGANIZATIONNAME = Facebook; TargetAttributes = { @@ -360,6 +412,11 @@ }; }; }; + 1EFEB5942493B6640072EDC0 = { + CreatedOnToolsVersion = 11.5; + DevelopmentTeam = S6UPZG7ZR3; + ProvisioningStyle = Manual; + }; }; }; buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "RocketChatRN" */; @@ -384,6 +441,7 @@ targets = ( 13B07F861A680F5B00A75B9A /* RocketChatRN */, 1EC6ACAF22CB9FC300A41C61 /* ShareRocketChatRN */, + 1EFEB5942493B6640072EDC0 /* NotificationService */, ); }; /* End PBXProject section */ @@ -421,6 +479,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 1EFEB5932493B6640072EDC0 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -720,6 +785,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 1EFEB5912493B6640072EDC0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1EFEB5982493B6640072EDC0 /* NotificationService.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -728,6 +801,11 @@ target = 1EC6ACAF22CB9FC300A41C61 /* ShareRocketChatRN */; targetProxy = 1EC6ACB922CB9FC300A41C61 /* PBXContainerItemProxy */; }; + 1EFEB59B2493B6640072EDC0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 1EFEB5942493B6640072EDC0 /* NotificationService */; + targetProxy = 1EFEB59A2493B6640072EDC0 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ @@ -978,6 +1056,68 @@ }; name = Release; }; + 1EFEB59D2493B6640072EDC0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; + CODE_SIGN_STYLE = Manual; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = S6UPZG7ZR3; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = NotificationService/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + MARKETING_VERSION = 4.10.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.reactnative.NotificationService; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = "chat.rocket.reactnative.NotificationService Development"; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 1EFEB59E2493B6640072EDC0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; + CODE_SIGN_IDENTITY = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = S6UPZG7ZR3; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = NotificationService/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + MARKETING_VERSION = 4.10.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.reactnative.NotificationService; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = "chat.rocket.reactnative.NotificationService AppStore"; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; 83CBBA201A601CBA00E9B192 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -1101,6 +1241,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 1EFEB5A02493B6640072EDC0 /* Build configuration list for PBXNativeTarget "NotificationService" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1EFEB59D2493B6640072EDC0 /* Debug */, + 1EFEB59E2493B6640072EDC0 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "RocketChatRN" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/ios/RocketChatRN.xcodeproj/xcshareddata/xcschemes/NotificationService.xcscheme b/ios/RocketChatRN.xcodeproj/xcshareddata/xcschemes/NotificationService.xcscheme new file mode 100644 index 00000000..3fd37eb6 --- /dev/null +++ b/ios/RocketChatRN.xcodeproj/xcshareddata/xcschemes/NotificationService.xcscheme @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/fastlane/Fastfile b/ios/fastlane/Fastfile index 3461925b..46de9f08 100644 --- a/ios/fastlane/Fastfile +++ b/ios/fastlane/Fastfile @@ -49,6 +49,7 @@ platform :ios do type: "appstore" ) # more information: https://codesigning.guide get_provisioning_profile(app_identifier: "chat.rocket.reactnative.ShareExtension") + get_provisioning_profile(app_identifier: "chat.rocket.reactnative.NotificationService") pem() gym(scheme: "RocketChatRN", workspace: "RocketChatRN.xcworkspace") # Build your app - more options available # frameit diff --git a/package.json b/package.json index 0a65e64d..057689a0 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,7 @@ "react-native-modal": "11.5.6", "react-native-navigation-bar-color": "2.0.1", "react-native-notifications": "2.1.7", - "react-native-notifier": "^1.3.1", + "react-native-notifier": "1.3.1", "react-native-orientation-locker": "1.1.8", "react-native-picker-select": "7.0.0", "react-native-platform-touchable": "^1.1.1", diff --git a/patches/react-native-notifications+2.1.7.patch b/patches/react-native-notifications+2.1.7.patch index 021a525b..fe34efcc 100644 --- a/patches/react-native-notifications+2.1.7.patch +++ b/patches/react-native-notifications+2.1.7.patch @@ -104,6 +104,19 @@ index edc4fd4..7cd77f6 100644 } @end +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 524ff07..70f22d5 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 +@@ -31,7 +31,7 @@ public class PushNotification implements IPushNotification { + final protected AppLifecycleFacade mAppLifecycleFacade; + final protected AppLaunchHelper mAppLaunchHelper; + final protected JsIOHelper mJsIOHelper; +- final protected PushNotificationProps mNotificationProps; ++ protected PushNotificationProps mNotificationProps; + final protected AppVisibilityListener mAppVisibilityListener = new AppVisibilityListener() { + @Override + public void onAppVisible() { diff --git a/node_modules/react-native-notifications/android/app/src/reactNative59/java/com/wix/reactnativenotifications/NotificationManagerCompatFacade.java b/node_modules/react-native-notifications/android/app/src/reactNative59/java/com/wix/reactnativenotifications/NotificationManagerCompatFacade.java index f9c858b..94ea188 100644 --- a/node_modules/react-native-notifications/android/app/src/reactNative59/java/com/wix/reactnativenotifications/NotificationManagerCompatFacade.java diff --git a/yarn.lock b/yarn.lock index 3d5cb670..7a7a307b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12845,7 +12845,7 @@ react-native-notifications@2.1.7: core-js "^1.0.0" uuid "^2.0.3" -react-native-notifier@^1.3.1: +react-native-notifier@1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/react-native-notifier/-/react-native-notifier-1.3.1.tgz#a878c82c8ee99b04d57818401b1f084232729afd" integrity sha512-w7KOTF5WOYzbhCXQHz6p9tbosOVxhOW+Sh7VAdIuW6r7PSoryRNkF4P6Bzq1+2NPtMK7L6xnojCdKJ+nVnwh+A==