Notification receipt delivery for Android and iOS (#2755)
* Notification receipt delivery for Android and iOS * Update Fastlane with iOS notification service extension * User JSONObject to build response
This commit is contained in:
parent
b746a170a7
commit
5bf96272ae
9 changed files with 435 additions and 22 deletions
|
|
@ -94,7 +94,13 @@ public class CustomPushNotification extends PushNotification {
|
|||
Bundle data = mNotificationProps.asBundle();
|
||||
final String channelId = data.getString("channel_id");
|
||||
final String type = data.getString("type");
|
||||
final String ackId = data.getString("ack_id");
|
||||
int notificationId = MESSAGE_NOTIFICATION_ID;
|
||||
|
||||
if (ackId != null) {
|
||||
notificationReceiptDelivery(ackId, type);
|
||||
}
|
||||
|
||||
if (channelId != null) {
|
||||
notificationId = channelId.hashCode();
|
||||
Object objCount = channelIdToNotificationCount.get(channelId);
|
||||
|
|
@ -379,4 +385,8 @@ public class CustomPushNotification extends PushNotification {
|
|||
String sender = String.format("%s: ", getSenderName(senderName, channelName, message));
|
||||
return message.replaceFirst(sender, "");
|
||||
}
|
||||
|
||||
private void notificationReceiptDelivery(String ackId, String type) {
|
||||
ReceiptDelivery.send(context, ackId, type);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,89 @@
|
|||
package com.mattermost.rnbeta;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
import java.lang.System;
|
||||
|
||||
import okhttp3.Call;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONException;
|
||||
|
||||
import com.mattermost.react_native_interface.ResolvePromise;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.oblador.keychain.KeychainModule;
|
||||
|
||||
public class ReceiptDelivery {
|
||||
public static void send (Context context, final String ackId, final String type) {
|
||||
final ReactApplicationContext reactApplicationContext = new ReactApplicationContext(context);
|
||||
final KeychainModule keychainModule = new KeychainModule(reactApplicationContext);
|
||||
|
||||
keychainModule.getGenericPasswordForOptions(null, new ResolvePromise() {
|
||||
@Override
|
||||
public void resolve(@Nullable Object value) {
|
||||
if (value instanceof Boolean && !(Boolean)value) {
|
||||
return;
|
||||
}
|
||||
|
||||
WritableMap map = (WritableMap) value;
|
||||
if (map != null) {
|
||||
String[] credentials = map.getString("password").split(",[ ]*");
|
||||
String token = null;
|
||||
String serverUrl = null;
|
||||
if (credentials.length == 2) {
|
||||
token = credentials[0];
|
||||
serverUrl = credentials[1];
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected static void execute(String serverUrl, String token, String ackId, String type) {
|
||||
if (token == null || serverUrl == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
JSONObject json;
|
||||
long receivedAt = System.currentTimeMillis();
|
||||
|
||||
try {
|
||||
json = new JSONObject();
|
||||
json.put("id", ackId);
|
||||
json.put("received_at", receivedAt);
|
||||
json.put("platform", "android");
|
||||
json.put("type", type);
|
||||
} catch (JSONException e) {
|
||||
Log.e("ReactNative", "Receipt delivery failed to build json payload");
|
||||
return;
|
||||
}
|
||||
|
||||
final OkHttpClient client = new OkHttpClient();
|
||||
final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
|
||||
RequestBody body = RequestBody.create(JSON, json.toString());
|
||||
Request request = new Request.Builder()
|
||||
.header("Authorization", String.format("Bearer %s", token))
|
||||
.header("Content-Type", "application/json")
|
||||
.url(String.format("%s/api/v4/notifications/ack", serverUrl.replaceAll("/$", "")))
|
||||
.post(body)
|
||||
.build();
|
||||
|
||||
try {
|
||||
client.newCall(request).execute();
|
||||
} catch (Exception e) {
|
||||
Log.e("ReactNative", "Receipt delivery failed to send");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -237,6 +237,20 @@ platform :ios do
|
|||
display_name: app_name
|
||||
)
|
||||
|
||||
# Set the notification service extension bundle identifier
|
||||
notification_bundle_id = ENV['NOTIFICATION_SERVICE_IDENTIFIER'] || 'com.mattermost.rnbeta.NotificationService'
|
||||
update_app_identifier(
|
||||
xcodeproj: './ios/Mattermost.xcodeproj',
|
||||
plist_path: 'NotificationService/Info.plist',
|
||||
app_identifier: notification_bundle_id
|
||||
)
|
||||
|
||||
find_replace_string(
|
||||
path_to_file: './ios/Mattermost.xcodeproj/project.pbxproj',
|
||||
old_string: 'com.mattermost.rnbeta.NotificationService',
|
||||
new_string: notification_bundle_id
|
||||
)
|
||||
|
||||
# Set the share extension bundle identifier
|
||||
extension_bundle_id = ENV['EXTENSION_APP_IDENTIFIER'] || 'com.mattermost.rnbeta.MattermostShare'
|
||||
update_app_identifier(
|
||||
|
|
@ -299,6 +313,11 @@ platform :ios do
|
|||
app_group_identifiers: [app_group_id]
|
||||
)
|
||||
|
||||
update_app_group_identifiers(
|
||||
entitlements_file: './ios/NotificationService/NotificationService.entitlements',
|
||||
app_group_identifiers: [app_group_id]
|
||||
)
|
||||
|
||||
# Sync the provisioning profiles using match
|
||||
if ENV['SYNC_PROVISIONING_PROFILES'] == 'true'
|
||||
match(type: ENV['MATCH_TYPE'] || 'adhoc')
|
||||
|
|
@ -381,11 +400,11 @@ platform :android do
|
|||
lane :configure_telemetry_android do
|
||||
if ENV['TELEMETRY_ENABLED'] == 'true' && ENV['TELEMETRY_URL'] != '' && ENV['TELEMETRY_API_KEY'] != ''
|
||||
# Update telemetry config
|
||||
json = load_config_json
|
||||
json = load_config_json('../dist/assets/config.json')
|
||||
json['TelemetryEnabled'] = true
|
||||
json['TelemetryUrl'] = ENV['TELEMETRY_URL']
|
||||
json['TelemetryApiKey'] = ENV['TELEMETRY_API_KEY']
|
||||
save_config_json(json)
|
||||
save_config_json('../dist/assets/config.json', json)
|
||||
|
||||
beta_dir = './android/app/src/main/java/com/mattermost/rnbeta/'
|
||||
|
||||
|
|
|
|||
|
|
@ -74,6 +74,9 @@
|
|||
7F43D6061F6BF9EB001FC614 /* libPods-Mattermost.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7F43D6051F6BF9EB001FC614 /* libPods-Mattermost.a */; };
|
||||
7F43D63F1F6BFA19001FC614 /* libBVLinearGradient.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 37D8FEC21E80B5230091F3BD /* libBVLinearGradient.a */; };
|
||||
7F43D6401F6BFA82001FC614 /* libRCTPushNotification.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7F63D2811E6C957C001FAE12 /* libRCTPushNotification.a */; };
|
||||
7F581D35221ED5C60099E66B /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F581D34221ED5C60099E66B /* NotificationService.swift */; };
|
||||
7F581D39221ED5C60099E66B /* NotificationService.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 7F581D32221ED5C60099E66B /* NotificationService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
7F581F78221EEA7C0099E66B /* libUploadAttachments.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7FABE04522137F2A00D0F595 /* libUploadAttachments.a */; };
|
||||
7F4C2598227E3B11009144EF /* libRNCNetInfo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7F4C2595227E3AE9009144EF /* libRNCNetInfo.a */; };
|
||||
7F5CA9A0208FE3B9004F91CE /* libRNDocumentPicker.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7F5CA991208FE38F004F91CE /* libRNDocumentPicker.a */; };
|
||||
7F642DF02093533300F3165E /* libRNDeviceInfo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7F642DED2093530B00F3165E /* libRNDeviceInfo.a */; };
|
||||
|
|
@ -475,6 +478,13 @@
|
|||
remoteGlobalIDString = ED296FEE214C9CF800B7C4FE;
|
||||
remoteInfo = "jsiexecutor-tvOS";
|
||||
};
|
||||
7F581D37221ED5C60099E66B /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 7F581D31221ED5C60099E66B;
|
||||
remoteInfo = NotificationService;
|
||||
};
|
||||
7F5CA990208FE38F004F91CE /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 7F5CA956208FE38F004F91CE /* RNDocumentPicker.xcodeproj */;
|
||||
|
|
@ -636,6 +646,13 @@
|
|||
remoteGlobalIDString = 5DBEB1501B18CEA900B34395;
|
||||
remoteInfo = RNVectorIcons;
|
||||
};
|
||||
7FE5F961227739E600FEFFE1 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 7FABE04022137F2900D0F595 /* UploadAttachments.xcodeproj */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 7FABE03522137F2900D0F595;
|
||||
remoteInfo = UploadAttachments;
|
||||
};
|
||||
7FE5FB3521A1EB710075D1AA /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 2B25899FDAC149EB96ED3305 /* RNVectorIcons.xcodeproj */;
|
||||
|
|
@ -691,6 +708,7 @@
|
|||
dstSubfolderSpec = 13;
|
||||
files = (
|
||||
7F240A23220D3A2300637665 /* MattermostShare.appex in Embed App Extensions */,
|
||||
7F581D39221ED5C60099E66B /* NotificationService.appex in Embed App Extensions */,
|
||||
);
|
||||
name = "Embed App Extensions";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
|
@ -786,6 +804,10 @@
|
|||
7F43D6051F6BF9EB001FC614 /* libPods-Mattermost.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libPods-Mattermost.a"; path = "../../../../../../../Library/Developer/Xcode/DerivedData/Mattermost-czlinsdviifujheezzjvmisotjrm/Build/Products/Debug-iphonesimulator/libPods-Mattermost.a"; sourceTree = "<group>"; };
|
||||
7F4C258F227E3AE9009144EF /* RNCNetInfo.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RNCNetInfo.xcodeproj; path = "../node_modules/@react-native-community/netinfo/ios/RNCNetInfo.xcodeproj"; sourceTree = "<group>"; };
|
||||
7F54ABFAE6CE4A6DB11D1ED7 /* Roboto-BlackItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Roboto-BlackItalic.ttf"; path = "../assets/fonts/Roboto-BlackItalic.ttf"; sourceTree = "<group>"; };
|
||||
7F581D32221ED5C60099E66B /* NotificationService.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NotificationService.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
7F581D34221ED5C60099E66B /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = "<group>"; };
|
||||
7F581D36221ED5C60099E66B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
7F581F77221EEA5A0099E66B /* NotificationService.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NotificationService.entitlements; sourceTree = "<group>"; };
|
||||
7F5CA956208FE38F004F91CE /* RNDocumentPicker.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RNDocumentPicker.xcodeproj; path = "../node_modules/react-native-document-picker/ios/RNDocumentPicker.xcodeproj"; sourceTree = "<group>"; };
|
||||
7F63D27B1E6C957C001FAE12 /* RCTPushNotification.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTPushNotification.xcodeproj; path = "../node_modules/react-native/Libraries/PushNotificationIOS/RCTPushNotification.xcodeproj"; sourceTree = "<group>"; };
|
||||
7F63D2C21E6DD98A001FAE12 /* Mattermost.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = Mattermost.entitlements; path = Mattermost/Mattermost.entitlements; sourceTree = "<group>"; };
|
||||
|
|
@ -919,6 +941,14 @@
|
|||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
7F581D2F221ED5C60099E66B /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
7F581F78221EEA7C0099E66B /* libUploadAttachments.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
|
|
@ -1049,7 +1079,6 @@
|
|||
13B07FAE1A68108700A75B9A /* Mattermost */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7F151D43221B082A00FAD8F3 /* Mattermost-Bridging-Header.h */,
|
||||
13B07FAF1A68108700A75B9A /* AppDelegate.h */,
|
||||
13B07FB01A68108700A75B9A /* AppDelegate.m */,
|
||||
7FEB10961F6101710039A015 /* BlurAppScreen.h */,
|
||||
|
|
@ -1061,6 +1090,7 @@
|
|||
7FF2AF8A2086483E00FFBDF4 /* KeyShareConsumer.storyboard */,
|
||||
13B07FB71A68108700A75B9A /* main.m */,
|
||||
7F63D2C21E6DD98A001FAE12 /* Mattermost.entitlements */,
|
||||
7F151D43221B082A00FAD8F3 /* Mattermost-Bridging-Header.h */,
|
||||
7F240ACB220D460300637665 /* MattermostBucketModule.h */,
|
||||
7F240ACC220D460300637665 /* MattermostBucketModule.m */,
|
||||
7FEB10991F61019C0039A015 /* MattermostManaged.h */,
|
||||
|
|
@ -1282,6 +1312,16 @@
|
|||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7F581D33221ED5C60099E66B /* NotificationService */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7F581F77221EEA5A0099E66B /* NotificationService.entitlements */,
|
||||
7F581D34221ED5C60099E66B /* NotificationService.swift */,
|
||||
7F581D36221ED5C60099E66B /* Info.plist */,
|
||||
);
|
||||
path = NotificationService;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7F5CA957208FE38F004F91CE /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
|
@ -1501,6 +1541,7 @@
|
|||
children = (
|
||||
13B07FAE1A68108700A75B9A /* Mattermost */,
|
||||
7F240A1A220D3A2300637665 /* MattermostShare */,
|
||||
7F581D33221ED5C60099E66B /* NotificationService */,
|
||||
7F292A701E8AB73400A450A3 /* SplashScreenResource */,
|
||||
832341AE1AAA6A7D00B99B32 /* Libraries */,
|
||||
00E356EF1AD99517003FC87E /* MattermostTests */,
|
||||
|
|
@ -1520,6 +1561,7 @@
|
|||
13B07F961A680F5B00A75B9A /* Mattermost.app */,
|
||||
00E356EE1AD99517003FC87E /* MattermostTests.xctest */,
|
||||
7F240A19220D3A2300637665 /* MattermostShare.appex */,
|
||||
7F581D32221ED5C60099E66B /* NotificationService.appex */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -1564,6 +1606,7 @@
|
|||
dependencies = (
|
||||
7FAB45BA222DD0E300EBFFC8 /* PBXTargetDependency */,
|
||||
7F240A22220D3A2300637665 /* PBXTargetDependency */,
|
||||
7F581D38221ED5C60099E66B /* PBXTargetDependency */,
|
||||
);
|
||||
name = Mattermost;
|
||||
productName = "Hello World";
|
||||
|
|
@ -1588,6 +1631,24 @@
|
|||
productReference = 7F240A19220D3A2300637665 /* MattermostShare.appex */;
|
||||
productType = "com.apple.product-type.app-extension";
|
||||
};
|
||||
7F581D31221ED5C60099E66B /* NotificationService */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 7F581D82221ED5C60099E66B /* Build configuration list for PBXNativeTarget "NotificationService" */;
|
||||
buildPhases = (
|
||||
7F581D2E221ED5C60099E66B /* Sources */,
|
||||
7F581D2F221ED5C60099E66B /* Frameworks */,
|
||||
7F581D30221ED5C60099E66B /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
7FE5F962227739E600FEFFE1 /* PBXTargetDependency */,
|
||||
);
|
||||
name = NotificationService;
|
||||
productName = NotificationService;
|
||||
productReference = 7F581D32221ED5C60099E66B /* NotificationService.appex */;
|
||||
productType = "com.apple.product-type.app-extension";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
|
|
@ -1634,6 +1695,16 @@
|
|||
};
|
||||
};
|
||||
};
|
||||
7F581D31221ED5C60099E66B = {
|
||||
CreatedOnToolsVersion = 10.1;
|
||||
DevelopmentTeam = UQ8HT4Q2XM;
|
||||
ProvisioningStyle = Automatic;
|
||||
SystemCapabilities = {
|
||||
com.apple.ApplicationGroups.iOS = {
|
||||
enabled = 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "Mattermost" */;
|
||||
|
|
@ -1815,6 +1886,7 @@
|
|||
13B07F861A680F5B00A75B9A /* Mattermost */,
|
||||
00E356ED1AD99517003FC87E /* MattermostTests */,
|
||||
7F240A18220D3A2300637665 /* MattermostShare */,
|
||||
7F581D31221ED5C60099E66B /* NotificationService */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
|
@ -2416,6 +2488,13 @@
|
|||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
7F581D30221ED5C60099E66B /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
|
|
@ -2522,6 +2601,14 @@
|
|||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
7F581D2E221ED5C60099E66B /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
7F581D35221ED5C60099E66B /* NotificationService.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
|
|
@ -2535,6 +2622,11 @@
|
|||
target = 7F240A18220D3A2300637665 /* MattermostShare */;
|
||||
targetProxy = 7F240A21220D3A2300637665 /* PBXContainerItemProxy */;
|
||||
};
|
||||
7F581D38221ED5C60099E66B /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 7F581D31221ED5C60099E66B /* NotificationService */;
|
||||
targetProxy = 7F581D37221ED5C60099E66B /* PBXContainerItemProxy */;
|
||||
};
|
||||
7FAB4571222DD0DA00EBFFC8 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
name = UploadAttachments;
|
||||
|
|
@ -2545,6 +2637,11 @@
|
|||
name = UploadAttachments;
|
||||
targetProxy = 7FAB45B9222DD0E300EBFFC8 /* PBXContainerItemProxy */;
|
||||
};
|
||||
7FE5F962227739E600FEFFE1 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
name = UploadAttachments;
|
||||
targetProxy = 7FE5F961227739E600FEFFE1 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
|
|
@ -2820,6 +2917,83 @@
|
|||
};
|
||||
name = Release;
|
||||
};
|
||||
7F581D3A221ED5C60099E66B /* 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_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
INFOPLIST_FILE = NotificationService/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.3;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.mattermost.rnbeta.NotificationService;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 4.2;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
7F581D3B221ED5C60099E66B /* 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_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
INFOPLIST_FILE = NotificationService/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.3;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.mattermost.rnbeta.NotificationService;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
SWIFT_VERSION = 4.2;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
83CBBA201A601CBA00E9B192 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
|
|
@ -2934,6 +3108,15 @@
|
|||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
7F581D82221ED5C60099E66B /* Build configuration list for PBXNativeTarget "NotificationService" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
7F581D3A221ED5C60099E66B /* Debug */,
|
||||
7F581D3B221ED5C60099E66B /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "Mattermost" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
|
|
|
|||
|
|
@ -120,11 +120,13 @@ NSString* const NotificationClearAction = @"clear";
|
|||
UIApplicationState state = [UIApplication sharedApplication].applicationState;
|
||||
NSString* action = [userInfo objectForKey:@"type"];
|
||||
NSString* channelId = [userInfo objectForKey:@"channel_id"];
|
||||
NSString* ackId = [userInfo objectForKey:@"ack_id"];
|
||||
|
||||
if (action && [action isEqualToString: NotificationClearAction]) {
|
||||
// If received a notification that a channel was read, remove all notifications from that channel (only with app in foreground/background)
|
||||
[self cleanNotificationsFromChannel:channelId andUpdateBadge:NO];
|
||||
RuntimeUtils *utils = [[RuntimeUtils alloc] init];
|
||||
[[UploadSession shared] notificationReceiptWithNotificationId:ackId receivedAt:round([[NSDate date] timeIntervalSince1970] * 1000.0) type:action];
|
||||
[utils delayWithSeconds:0.2 closure:^(void) {
|
||||
// This is to notify the NotificationCenter that something has changed.
|
||||
completionHandler(UIBackgroundFetchResultNewData);
|
||||
|
|
|
|||
31
ios/NotificationService/Info.plist
Normal file
31
ios/NotificationService/Info.plist
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>NotificationService</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.19.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>188</string>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
<string>com.apple.usernotifications.service</string>
|
||||
<key>NSExtensionPrincipalClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).NotificationService</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
10
ios/NotificationService/NotificationService.entitlements
Normal file
10
ios/NotificationService/NotificationService.entitlements
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.com.mattermost.rnbeta</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
42
ios/NotificationService/NotificationService.swift
Normal file
42
ios/NotificationService/NotificationService.swift
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import UserNotifications
|
||||
import UploadAttachments
|
||||
|
||||
class NotificationService: UNNotificationServiceExtension {
|
||||
|
||||
var contentHandler: ((UNNotificationContent) -> Void)?
|
||||
var bestAttemptContent: UNMutableNotificationContent?
|
||||
|
||||
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
|
||||
self.contentHandler = contentHandler
|
||||
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
|
||||
|
||||
if let bestAttemptContent = bestAttemptContent {
|
||||
UploadSession.shared.notificationReceipt(
|
||||
notificationId: bestAttemptContent.userInfo["ack_id"],
|
||||
receivedAt: Date().millisencondsSince1970,
|
||||
type: bestAttemptContent.userInfo["type"]
|
||||
)
|
||||
|
||||
contentHandler(bestAttemptContent)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension Date {
|
||||
var millisencondsSince1970: Int {
|
||||
return Int((self.timeIntervalSince1970 * 1000.0).rounded())
|
||||
}
|
||||
|
||||
init(milliseconds: Int) {
|
||||
self = Date(timeIntervalSince1970: TimeInterval(milliseconds) / 1000)
|
||||
}
|
||||
}
|
||||
|
|
@ -38,9 +38,9 @@ import os.log
|
|||
if #available(iOS 12.0, *) {
|
||||
os_log(OSLogType.default, "Mattermost will post identifier=%{public}@", identifier)
|
||||
}
|
||||
|
||||
|
||||
URLSession(configuration: .ephemeral).dataTask(with: request).resume()
|
||||
|
||||
|
||||
UploadSessionManager.shared.removeUploadSessionData(identifier: identifier)
|
||||
UploadSessionManager.shared.clearTempDirectory()
|
||||
}
|
||||
|
|
@ -56,7 +56,7 @@ import os.log
|
|||
if #available(iOS 11.0, *) {
|
||||
sessionConfig.waitsForConnectivity = true
|
||||
}
|
||||
|
||||
|
||||
session = URLSession(configuration: sessionConfig, delegate: self, delegateQueue: OperationQueue.main)
|
||||
}
|
||||
|
||||
|
|
@ -66,32 +66,32 @@ import os.log
|
|||
if #available(iOS 11.0, *) {
|
||||
sessionConfig.waitsForConnectivity = true
|
||||
}
|
||||
|
||||
|
||||
self.session = URLSession(configuration: sessionConfig, delegate: self, delegateQueue: nil)
|
||||
if #available(iOS 12.0, *) {
|
||||
os_log(OSLogType.default, "Mattermost Session created identifier=%{public}@", identifier)
|
||||
}
|
||||
return self.session!
|
||||
}
|
||||
|
||||
|
||||
public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
|
||||
// here we should get the file Id and update it in the session
|
||||
guard let identifier = session.configuration.identifier else {return}
|
||||
do {
|
||||
let jsonObject = try JSONSerialization.jsonObject(with: data, options: []) as! NSDictionary
|
||||
let fileInfos = jsonObject.object(forKey: "file_infos") as! NSArray
|
||||
if fileInfos.count > 0 {
|
||||
let fileInfoData = fileInfos[0] as! NSDictionary
|
||||
let fileId = fileInfoData.object(forKey: "id") as! String
|
||||
UploadSessionManager.shared.appendCompletedUploadToSession(identifier: identifier, fileId: fileId)
|
||||
}
|
||||
} catch {
|
||||
if #available(iOS 12.0, *) {
|
||||
os_log(OSLogType.default, "Mattermost Failed to receive data identifier=%{public}@ error=%{public}", identifier, error.localizedDescription)
|
||||
}
|
||||
print("MMLOG: Failed to get the file upload response %@", error.localizedDescription)
|
||||
do {
|
||||
let jsonObject = try JSONSerialization.jsonObject(with: data, options: []) as! NSDictionary
|
||||
let fileInfos = jsonObject.object(forKey: "file_infos") as! NSArray
|
||||
if fileInfos.count > 0 {
|
||||
let fileInfoData = fileInfos[0] as! NSDictionary
|
||||
let fileId = fileInfoData.object(forKey: "id") as! String
|
||||
UploadSessionManager.shared.appendCompletedUploadToSession(identifier: identifier, fileId: fileId)
|
||||
}
|
||||
|
||||
} catch {
|
||||
if #available(iOS 12.0, *) {
|
||||
os_log(OSLogType.default, "Mattermost Failed to receive data identifier=%{public}@ error=%{public}", identifier, error.localizedDescription)
|
||||
}
|
||||
print("MMLOG: Failed to get the file upload response %@", error.localizedDescription)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
|
||||
|
|
@ -123,4 +123,31 @@ import os.log
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
public func notificationReceipt(notificationId: Any?, receivedAt: Int, type: Any?) {
|
||||
let store = StoreManager.shared() as StoreManager
|
||||
let _ = store.getEntities(true)
|
||||
let serverURL = store.getServerUrl()
|
||||
let sessionToken = store.getToken()
|
||||
let urlString = "\(serverURL!)/api/v4/notifications/ack"
|
||||
|
||||
if (notificationId != nil) {
|
||||
let jsonObject: [String: Any] = [
|
||||
"id": notificationId as Any,
|
||||
"received_at": receivedAt,
|
||||
"platform": "ios",
|
||||
"type": type 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue