diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ecc431..c5c9ee6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,17 @@ All notable changes to this project will be documented in this file. +## [0.8.0] - 2024-01-16 + +IMPORTANT: +Due to changes in the handling of actions for URLs and deep links in Synerise campaigns, we strongly recommend comparing your configuration with the SDK documentation. Review the changes from the previous SDK version integrated into your application here: +https://hub.synerise.com//developers/mobile-sdk/campaigns/action-handling/ + +### Changed +- Changes in handling actions from campaigns (read important note above). +- Update of native SDK's dependencies. + + ## [0.17.0] - 2023-12-05 ### Fixed diff --git a/README.md b/README.md index 4b80719..d1aa83e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Synerise React Native SDK (react-native-synerise-sdk) (0.17.0) +# Synerise React Native SDK (react-native-synerise-sdk) (0.18.0) [![Platform](https://img.shields.io/badge/platform-iOS-orange.svg)](https://github.com/synerise/ios-sdk) [![Platform](https://img.shields.io/badge/platform-Android-orange.svg)](https://github.com/synerise/android-sdk) diff --git a/android/build.gradle b/android/build.gradle index b32789b..f17eefd 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -4,8 +4,8 @@ ext.versions = [ 'minSdk' : 21, 'compileSdk' : 33, 'targetSdk' : 33, - 'versionCode': 31, - 'versionName': "0.17.0" + 'versionCode': 32, + 'versionName': "0.18.0" ] buildscript { @@ -57,7 +57,7 @@ repositories { dependencies { implementation 'com.facebook.react:react-native:+' implementation "com.squareup.okhttp3:logging-interceptor:4.9.1" - api 'com.synerise.sdk:synerise-mobile-sdk:5.12.0' + api 'com.synerise.sdk:synerise-mobile-sdk:5.13.2' } //apply from: 'publish.gradle' \ No newline at end of file diff --git a/android/src/main/java/com/synerise/sdk/react/RNBaseModule.java b/android/src/main/java/com/synerise/sdk/react/RNBaseModule.java index 990cfcf..33c46d3 100644 --- a/android/src/main/java/com/synerise/sdk/react/RNBaseModule.java +++ b/android/src/main/java/com/synerise/sdk/react/RNBaseModule.java @@ -1,5 +1,7 @@ package com.synerise.sdk.react; +import android.util.Log; + import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.Callback; import com.facebook.react.bridge.ReactApplicationContext; diff --git a/android/src/main/java/com/synerise/sdk/react/RNInjector.java b/android/src/main/java/com/synerise/sdk/react/RNInjector.java index 9e6abd3..5151bdc 100644 --- a/android/src/main/java/com/synerise/sdk/react/RNInjector.java +++ b/android/src/main/java/com/synerise/sdk/react/RNInjector.java @@ -1,5 +1,6 @@ package com.synerise.sdk.react; +import android.os.Handler; import android.util.Log; import com.facebook.react.bridge.Arguments; @@ -10,7 +11,9 @@ import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableMap; import com.google.gson.Gson; +import com.synerise.sdk.core.Synerise; import com.synerise.sdk.core.listeners.DataActionListener; +import com.synerise.sdk.core.utils.SystemUtils; import com.synerise.sdk.error.ApiError; import com.synerise.sdk.injector.Injector; import com.synerise.sdk.injector.callback.InjectorSource; @@ -42,6 +45,7 @@ public class RNInjector extends RNBaseModule { private static final String DEEP_LINK_KEY = "DEEPLINK_ACTION_LISTENER_KEY"; private static final String OPEN_URL_VALUE = "openUrl"; private static final String DEEP_LINK = "deepLink"; + private static final String SOURCE = "source"; private static final String DEEP_LINK_VALUE = "deepLink"; private static final String BANNER_PRESENTED_KEY = "BANNER_PRESENTED_LISTENER_KEY"; private static final String BANNER_HIDDEN_KEY = "BANNER_HIDDEN_LISTENER_KEY"; @@ -98,6 +102,16 @@ public void setBannerShouldPresentFlag(boolean flag) { shouldBannerPresentFlag = flag; } + @ReactMethod + public void handleOpenUrlBySDK(String url) { + SystemUtils.openURL(Synerise.getApplicationContext(), url); + } + + @ReactMethod + public void handleDeepLinkBySDK(String deepLink) { + SystemUtils.openDeepLink(Synerise.getApplicationContext(), deepLink); + } + @Nullable @Override public Map getConstants() { @@ -126,39 +140,54 @@ public String getName() { protected static void initializeInjector() { initializeInAppListener(); - initializeActionInjectorListener(); initializeBannerListener(); initializeWalkthroughListener(); } - private static void onActionOpenUrl(String url) { - WritableMap data = Arguments.createMap(); - data.putString(URL, url); - sendEventToJs(OPEN_URL_VALUE, data, reactApplicationContext); - } - - private static void onActionDeepLink(String deepLink) { - WritableMap data = Arguments.createMap(); - data.putString(DEEP_LINK, deepLink); - sendEventToJs(DEEP_LINK_VALUE, data, reactApplicationContext); - } - - private static void initializeActionInjectorListener() { + protected static void initializeActionInjectorListener() { InjectorActionHandler.setOnInjectorListener(new OnInjectorListener() { @Override public boolean onOpenUrl(InjectorSource injectorSource, String url) { - onActionOpenUrl(url); + Handler handler = new android.os.Handler(); + handler.postDelayed(new Runnable() { + @Override + public void run() { + onActionOpenUrl(url, injectorSource.name()); + } + }, 300); + return injectorSource != InjectorSource.WALKTHROUGH; } @Override public boolean onDeepLink(InjectorSource injectorSource, String deepLink) { - onActionDeepLink(deepLink); + Handler handler = new android.os.Handler(); + handler.postDelayed(new Runnable() { + @Override + public void run() { + onActionDeepLink(deepLink, injectorSource.name()); + } + }, 300); + return injectorSource != InjectorSource.WALKTHROUGH; } }); } + private static void onActionOpenUrl(String url, String source) { + WritableMap data = Arguments.createMap(); + data.putString(URL, url); + data.putString(SOURCE, source); + sendEventToJs(OPEN_URL_VALUE, data, reactApplicationContext); + } + + private static void onActionDeepLink(String deepLink, String source) { + WritableMap data = Arguments.createMap(); + data.putString(DEEP_LINK, deepLink); + data.putString(SOURCE, source); + sendEventToJs(DEEP_LINK_VALUE, data, reactApplicationContext); + } + private static void initializeInAppListener() { Injector.setOnInAppListener(new OnInAppListener() { @Override diff --git a/android/src/main/java/com/synerise/sdk/react/RNNotifications.java b/android/src/main/java/com/synerise/sdk/react/RNNotifications.java index 259c0ba..2af2ed6 100644 --- a/android/src/main/java/com/synerise/sdk/react/RNNotifications.java +++ b/android/src/main/java/com/synerise/sdk/react/RNNotifications.java @@ -3,6 +3,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; +import android.util.Log; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.Callback; diff --git a/android/src/main/java/com/synerise/sdk/react/RNSyneriseInitializer.java b/android/src/main/java/com/synerise/sdk/react/RNSyneriseInitializer.java index 55d049e..26513ab 100644 --- a/android/src/main/java/com/synerise/sdk/react/RNSyneriseInitializer.java +++ b/android/src/main/java/com/synerise/sdk/react/RNSyneriseInitializer.java @@ -3,6 +3,7 @@ import android.app.Application; import android.content.Context; import android.content.pm.ApplicationInfo; +import android.util.Log; import com.synerise.sdk.core.Synerise; import com.synerise.sdk.core.types.enums.HostApplicationType; @@ -16,12 +17,12 @@ public class RNSyneriseInitializer { public Boolean isCrashHandlingEnabled; public static volatile boolean isInitialized = false; - public static final String SDK_PLUGIN_VERSION = "0.17.0"; + public static final String SDK_PLUGIN_VERSION = "0.18.0"; public void initialize(Application app) { if (isInitialized == false) { prepareDefaultSettings(); - + RNInjector.initializeActionInjectorListener(); Synerise.Builder.with(app, clientApiKey, getApplicationName(app)) .baseUrl(baseUrl) .syneriseDebugMode(isDebugModeEnabled) @@ -31,7 +32,6 @@ public void initialize(Application app) { .hostApplicationType(HostApplicationType.REACT_NATIVE) .hostApplicationSDKPluginVersion(SDK_PLUGIN_VERSION) .build(); - // Client.registerForPushCallback(RNNotifications.getNativePushListener()); isInitialized = true; } diff --git a/ios/ReactNativeSynerise/Main/RNSyneriseInitializer.m b/ios/ReactNativeSynerise/Main/RNSyneriseInitializer.m index b6126c7..833dcf5 100644 --- a/ios/ReactNativeSynerise/Main/RNSyneriseInitializer.m +++ b/ios/ReactNativeSynerise/Main/RNSyneriseInitializer.m @@ -8,7 +8,7 @@ #import "RNSyneriseInitializer.h" -NSString * const SNRSyneriseSDKPluginVersion = @"0.17.0"; +NSString * const SNRSyneriseSDKPluginVersion = @"0.18.0"; @implementation RNSyneriseInitializer diff --git a/ios/ReactNativeSynerise/Modules/RNInjector.h b/ios/ReactNativeSynerise/Modules/RNInjector.h index 3c30d02..a18db99 100644 --- a/ios/ReactNativeSynerise/Modules/RNInjector.h +++ b/ios/ReactNativeSynerise/Modules/RNInjector.h @@ -12,8 +12,8 @@ NS_ASSUME_NONNULL_BEGIN @interface RNInjector : RNBaseModule -- (void)executeURLAction:(NSURL *)URL; -- (void)executeDeepLinkAction:(NSString *)deepLink; +- (void)executeURLAction:(NSURL *)URL activity:(SNRSyneriseActivity)activity; +- (void)executeDeepLinkAction:(NSString *)deepLink activity:(SNRSyneriseActivity)activity; @end diff --git a/ios/ReactNativeSynerise/Modules/RNInjector.m b/ios/ReactNativeSynerise/Modules/RNInjector.m index f16adc9..6c25cc4 100644 --- a/ios/ReactNativeSynerise/Modules/RNInjector.m +++ b/ios/ReactNativeSynerise/Modules/RNInjector.m @@ -62,26 +62,28 @@ - (instancetype)init { #pragma mark - Public -- (void)executeURLAction:(NSURL *)URL { - [self sendURLActionToJS:URL]; +- (void)executeURLAction:(NSURL *)URL activity:(SNRSyneriseActivity)activity { + [self sendURLActionToJS:URL activity:activity]; } -- (void)executeDeepLinkAction:(NSString *)deepLink { - [self sendDeepLinkActionToJS:deepLink]; +- (void)executeDeepLinkAction:(NSString *)deepLink activity:(SNRSyneriseActivity)activity { + [self sendDeepLinkActionToJS:deepLink activity:activity]; } #pragma mark - Private -- (void)sendURLActionToJS:(NSURL *)URL { +- (void)sendURLActionToJS:(NSURL *)URL activity:(SNRSyneriseActivity)activity { NSDictionary *userInfo = @{ - @"url": [URL absoluteString] + @"url": [URL absoluteString], + @"source": [self stringWithSyneriseActivity:activity] }; [[NSNotificationCenter defaultCenter] postNotificationName:kRNSyneriseUrlActionEvent object:nil userInfo:userInfo]; } -- (void)sendDeepLinkActionToJS:(NSString *)deepLink { +- (void)sendDeepLinkActionToJS:(NSString *)deepLink activity:(SNRSyneriseActivity)activity { NSDictionary *userInfo = @{ - @"deepLink": deepLink + @"deepLink": deepLink, + @"source": [self stringWithSyneriseActivity:activity] }; [[NSNotificationCenter defaultCenter] postNotificationName:kRNSyneriseDeepLinkActionEvent object:nil userInfo:userInfo]; } @@ -234,6 +236,20 @@ - (void)SNR_inAppMessageHandledCustomAction:(SNRInAppMessageData *)data name:(NS #pragma mark - JS Mapping +- (NSString *)stringWithSyneriseActivity:(SNRSyneriseActivity)activity { + if (activity == SNRSyneriseActivitySimplePush) { + return @"SIMPLE_PUSH"; + } else if (activity == SNRSyneriseActivityBanner) { + return @"BANNER"; + } else if (activity == SNRSyneriseActivityWalkthrough) { + return @"WALKTHROUGH"; + } else if (activity == SNRSyneriseActivityInAppMessage) { + return @"IN_APP_MESSAGE"; + } else { + return @"NOT_SPECIFIED"; + } +} + - (nullable NSDictionary *)dictionaryWithInAppMessageData:(nullable SNRInAppMessageData *)model { if (model != nil) { NSMutableDictionary *dictionary = [@{} mutableCopy]; @@ -273,11 +289,38 @@ - (NSDictionary *)constantsToExport }; } -//setBannerShouldPresentFlag(flag: boolean) +//handleOpenUrlBySDK(url: string) + +RCT_EXPORT_METHOD(handleOpenUrlBySDK:(nonnull NSString *)urlString) +{ + dispatch_async(dispatch_get_main_queue(), ^{ + NSURL *URL = [NSURL URLWithString:urlString]; + if ([[UIApplication sharedApplication] canOpenURL:URL]) { + if (@available(iOS 10, *)) { + [[UIApplication sharedApplication] openURL:URL options:@{} completionHandler:nil]; + } else { + [[UIApplication sharedApplication] openURL:URL]; + } + } + }); +} + +//handleDeepLinkBySDK(deepLink: string) -RCT_EXPORT_METHOD(setBannerShouldPresentFlag:(nonnull NSNumber *)flag) +RCT_EXPORT_METHOD(handleDeepLinkBySDK:(nonnull NSString *)deepLink) { - _bannerShouldPresentFlag = [flag boolValue]; + dispatch_async(dispatch_get_main_queue(), ^{ + NSURL *deepLinkURL = [NSURL URLWithString:deepLink]; + if ([[UIApplication sharedApplication] canOpenURL:deepLinkURL]) { + if (@available(iOS 10, *)) { + [[UIApplication sharedApplication] openURL:deepLinkURL options:@{} completionHandler:^(BOOL success) { + + }]; + } else { + [[UIApplication sharedApplication] openURL:deepLinkURL]; + } + } + }); } //getWalkthrough() diff --git a/ios/ReactNativeSynerise/Modules/RNSynerise.m b/ios/ReactNativeSynerise/Modules/RNSynerise.m index 7e327c8..c037c58 100644 --- a/ios/ReactNativeSynerise/Modules/RNSynerise.m +++ b/ios/ReactNativeSynerise/Modules/RNSynerise.m @@ -74,22 +74,14 @@ - (void)SNR_registerForPushNotificationsIsNeeded { } - (void)SNR_handledActionWithURL:(NSURL *)url activity:(SNRSyneriseActivity)activity completionHandler:(SNRSyneriseActivityCompletionHandler)completionHandler { - if (activity == SNRSyneriseActivityInAppMessage) { - return; - } - - completionHandler(SNRSyneriseActivityActionHide, ^{ - [[RNSyneriseManager sharedInstance].injector executeURLAction:url]; + completionHandler(SNRSyneriseActivityActionNone, ^{ + [[RNSyneriseManager sharedInstance].injector executeURLAction:url activity:activity]; }); } - (void)SNR_handledActionWithDeepLink:(NSString *)deepLink activity:(SNRSyneriseActivity)activity completionHandler:(SNRSyneriseActivityCompletionHandler)completionHandler { - if (activity == SNRSyneriseActivityInAppMessage) { - return; - } - - completionHandler(SNRSyneriseActivityActionHide, ^{ - [[RNSyneriseManager sharedInstance].injector executeDeepLinkAction:deepLink]; + completionHandler(SNRSyneriseActivityActionNone, ^{ + [[RNSyneriseManager sharedInstance].injector executeDeepLinkAction:deepLink activity:activity]; }); } diff --git a/ios/react-native-synerise-sdk.podspec b/ios/react-native-synerise-sdk.podspec index 836fde6..5a0d014 100644 --- a/ios/react-native-synerise-sdk.podspec +++ b/ios/react-native-synerise-sdk.podspec @@ -9,7 +9,7 @@ Pod::Spec.new do |s| s.version = package['version'] s.summary = package['description'] s.homepage = "https://synerise.com" - s.license = "MIT" + s.license = { :type => "Apache License 2.0", :file => "LICENSE" } s.author = { "Synerise" => "developer@synerise.com" } s.platform = :ios, "11.0" s.source = { :git => "https://github.com/Synerise/react-native-synerise-sdk", :tag => s.version.to_s } diff --git a/lib/config/import_models.d.ts b/lib/config/import_models.d.ts index 9e674f5..d9754ed 100644 --- a/lib/config/import_models.d.ts +++ b/lib/config/import_models.d.ts @@ -47,4 +47,5 @@ export { DocumentsApiQuery } from './../classes/api_queries/DocumentsApiQuery'; export { DocumentsApiQueryType } from './../classes/api_queries/DocumentsApiQueryType'; export { Token } from './../classes/models/Token/Token'; export { TokenOrigin, TokenOriginFromString, TokenOriginToString } from './../classes/models/Token/TokenOrigin'; +export { SyneriseSource } from './../classes/models/Misc/SyneriseSource'; export { InAppMessageData } from './../classes/models/Misc/InAppMessageData'; diff --git a/lib/config/import_models.js b/lib/config/import_models.js index 5dec63a..d4371a0 100644 --- a/lib/config/import_models.js +++ b/lib/config/import_models.js @@ -116,5 +116,7 @@ Object.defineProperty(exports, "TokenOrigin", { enumerable: true, get: function Object.defineProperty(exports, "TokenOriginFromString", { enumerable: true, get: function () { return TokenOrigin_1.TokenOriginFromString; } }); Object.defineProperty(exports, "TokenOriginToString", { enumerable: true, get: function () { return TokenOrigin_1.TokenOriginToString; } }); // MISC +var SyneriseSource_1 = require("./../classes/models/Misc/SyneriseSource"); +Object.defineProperty(exports, "SyneriseSource", { enumerable: true, get: function () { return SyneriseSource_1.SyneriseSource; } }); var InAppMessageData_1 = require("./../classes/models/Misc/InAppMessageData"); Object.defineProperty(exports, "InAppMessageData", { enumerable: true, get: function () { return InAppMessageData_1.InAppMessageData; } }); diff --git a/lib/main/modules/InjectorModule.d.ts b/lib/main/modules/InjectorModule.d.ts index 928793b..2453a09 100644 --- a/lib/main/modules/InjectorModule.d.ts +++ b/lib/main/modules/InjectorModule.d.ts @@ -1,9 +1,10 @@ import { BaseModule as Module } from './BaseModule'; import { InAppMessageData } from './../../classes/models/Misc/InAppMessageData'; import { Error } from './../../classes/types/Error'; +import { SyneriseSource } from '../../classes/models/Misc/SyneriseSource'; interface IInjectorListener { - onOpenUrl(url: string): void; - onDeepLink(deepLink: string): void; + onOpenUrl(url: string, source: SyneriseSource): void; + onDeepLink(deepLink: string, source: SyneriseSource): void; } interface IInjectorBannerListener { onPresent?(): void; @@ -105,5 +106,7 @@ declare class InjectorModule extends Module { * @returns `true` if the loaded walkthrough is unique, otherwise returns `false` */ isLoadedWalkthroughUnique(): boolean; + handleOpenUrlBySDK(url: string): void; + handleDeepLinkBySDK(deepLink: string): void; } export { InjectorModule, IInjectorListener }; diff --git a/lib/main/modules/InjectorModule.js b/lib/main/modules/InjectorModule.js index 9b8ef73..cb53feb 100644 --- a/lib/main/modules/InjectorModule.js +++ b/lib/main/modules/InjectorModule.js @@ -20,6 +20,7 @@ var SyneriseModuleConnector_1 = require("./../communication/SyneriseModuleConnec var SyneriseModuleEmitter_1 = require("./../communication/SyneriseModuleEmitter"); var InAppMessageData_1 = require("./../../classes/models/Misc/InAppMessageData"); var Error_1 = require("./../../classes/types/Error"); +var SyneriseSource_1 = require("../../classes/models/Misc/SyneriseSource"); var RNInjector = react_native_1.NativeModules.RNInjector; var InjectorModule = /** @class */ (function (_super) { __extends(InjectorModule, _super); @@ -27,12 +28,14 @@ var InjectorModule = /** @class */ (function (_super) { var _this = _super.call(this) || this; _this.onUrlAction = function (event) { if (_this.listener !== undefined) { - _this.listener.onOpenUrl(event.url); + var source = SyneriseSource_1.SyneriseSourceFromString(event.source); + _this.listener.onOpenUrl(event.url, source); } }; _this.onDeepLinkAction = function (event) { if (_this.listener !== undefined) { - _this.listener.onDeepLink(event.deepLink); + var source = SyneriseSource_1.SyneriseSourceFromString(event.source); + _this.listener.onDeepLink(event.deepLink, source); } }; _this.onBannerPresent = function () { @@ -101,6 +104,14 @@ var InjectorModule = /** @class */ (function (_super) { } }; _this.configureListeners(); + _this.setListener({ + onOpenUrl: function (url, source) { + SyneriseModuleConnector_1.SyneriseModuleConnector.invokeMethod(RNInjector.handleOpenUrlBySDK, [url]); + }, + onDeepLink: function (deepLink, source) { + SyneriseModuleConnector_1.SyneriseModuleConnector.invokeMethod(RNInjector.handleDeepLinkBySDK, [deepLink]); + } + }); return _this; } InjectorModule.instance = function () { @@ -211,6 +222,12 @@ var InjectorModule = /** @class */ (function (_super) { InjectorModule.prototype.isLoadedWalkthroughUnique = function () { return SyneriseModuleConnector_1.SyneriseModuleConnector.invokeMethodWithReturnValue(RNInjector.isLoadedWalkthroughUnique, []); }; + InjectorModule.prototype.handleOpenUrlBySDK = function (url) { + SyneriseModuleConnector_1.SyneriseModuleConnector.invokeMethod(RNInjector.handleOpenUrlBySDK, [url]); + }; + InjectorModule.prototype.handleDeepLinkBySDK = function (deepLink) { + SyneriseModuleConnector_1.SyneriseModuleConnector.invokeMethod(RNInjector.handleDeepLinkBySDK, [deepLink]); + }; return InjectorModule; }(BaseModule_1.BaseModule)); exports.InjectorModule = InjectorModule; diff --git a/package.json b/package.json index d5053ad..e634e6d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-synerise-sdk", - "version": "0.17.0", + "version": "0.18.0", "description": "React Native wrapper for Synerise SDK", "author": { "name": "Synerise", @@ -37,6 +37,6 @@ }, "devDependencies": { "prettier": "^1.18.2", - "typescript": "^3.6.4" + "typescript": "^3.9.9" } } diff --git a/react-native-synerise-sdk.podspec b/react-native-synerise-sdk.podspec index ef24573..99ded15 100644 --- a/react-native-synerise-sdk.podspec +++ b/react-native-synerise-sdk.podspec @@ -2,14 +2,14 @@ require 'json' package = JSON.parse(File.read('./package.json')) -SYNERISE_SDK_FRAMEWORK_VERSION = '4.14.7' +SYNERISE_SDK_FRAMEWORK_VERSION = '4.14.9' Pod::Spec.new do |s| s.name = package['name'] s.version = package['version'] s.summary = package['description'] s.homepage = "https://synerise.com" - s.license = "MIT" + s.license = { :type => "Apache License 2.0", :file => "LICENSE" } s.author = { "Synerise" => "developer@synerise.com" } s.platform = :ios, "11.0" s.source = { :git => "https://github.com/Synerise/react-native-synerise-sdk", :tag => s.version.to_s }