pushwoosh-react-native-plugin 7.0.1 → 7.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/pushwoosh-react-native-plugin.podspec +2 -2
- package/src/android/build.gradle +1 -1
- package/src/android/src/main/java/com/pushwoosh/reactnativeplugin/ConversionUtil.java +9 -1
- package/src/android/src/main/java/com/pushwoosh/reactnativeplugin/InboxUiStyleManager.java +85 -43
- package/src/android/src/main/java/com/pushwoosh/reactnativeplugin/PushwooshPlugin.java +8 -5
- package/src/android/src/main/java/com/pushwoosh/reactnativeplugin/internal/ReactNativePluginProvider.java +7 -0
- package/src/ios/PushwooshPlugin/Pushwoosh.mm +166 -19
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Pod::Spec.new do |s|
|
|
2
2
|
s.name = "pushwoosh-react-native-plugin"
|
|
3
|
-
s.version = "7.0.
|
|
3
|
+
s.version = "7.0.3"
|
|
4
4
|
s.summary = "React Native Pushwoosh Push Notifications module"
|
|
5
5
|
s.requires_arc = true
|
|
6
6
|
s.author = 'Pushwoosh'
|
|
@@ -19,6 +19,6 @@ Pod::Spec.new do |s|
|
|
|
19
19
|
s.dependency "React-Core"
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
s.dependency 'PushwooshXCFramework', '7.2.
|
|
22
|
+
s.dependency 'PushwooshXCFramework', '7.2.8'
|
|
23
23
|
s.dependency 'PushwooshInboxUIXCFramework', '7.0.42'
|
|
24
24
|
end
|
package/src/android/build.gradle
CHANGED
|
@@ -31,8 +31,12 @@ public final class ConversionUtil {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
public static JSONObject toJsonObject(ReadableMap readableMap) {
|
|
34
|
-
ReadableMapKeySetIterator iterator = readableMap.keySetIterator();
|
|
35
34
|
JSONObject result = new JSONObject();
|
|
35
|
+
if (readableMap == null) {
|
|
36
|
+
PWLog.error(PushwooshPlugin.TAG, "Could not convert object: map is null.");
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
ReadableMapKeySetIterator iterator = readableMap.keySetIterator();
|
|
36
40
|
while (iterator.hasNextKey()) {
|
|
37
41
|
String key = iterator.nextKey();
|
|
38
42
|
ReadableType type = readableMap.getType(key);
|
|
@@ -215,6 +219,10 @@ public final class ConversionUtil {
|
|
|
215
219
|
|
|
216
220
|
public static ArrayList<String> messageCodesArrayToArrayList(ReadableArray readableArray) {
|
|
217
221
|
ArrayList<String> result = new ArrayList<>();
|
|
222
|
+
if (readableArray == null) {
|
|
223
|
+
PWLog.error(PushwooshPlugin.TAG, "Could not convert message codes: array is null.");
|
|
224
|
+
return result;
|
|
225
|
+
}
|
|
218
226
|
for (int i = 0; i < readableArray.size(); i++) {
|
|
219
227
|
ReadableType indexType = readableArray.getType(i);
|
|
220
228
|
try {
|
|
@@ -7,8 +7,10 @@ import android.graphics.drawable.BitmapDrawable;
|
|
|
7
7
|
import android.graphics.drawable.Drawable;
|
|
8
8
|
|
|
9
9
|
import com.facebook.react.bridge.ReadableMap;
|
|
10
|
+
import com.facebook.react.bridge.ReadableType;
|
|
10
11
|
import com.pushwoosh.inbox.ui.PushwooshInboxStyle;
|
|
11
12
|
import com.pushwoosh.inbox.ui.model.customizing.formatter.InboxDateFormatter;
|
|
13
|
+
import com.pushwoosh.internal.utils.PWLog;
|
|
12
14
|
|
|
13
15
|
import java.io.IOException;
|
|
14
16
|
import java.net.URL;
|
|
@@ -53,22 +55,45 @@ class InboxUiStyleManager {
|
|
|
53
55
|
}
|
|
54
56
|
|
|
55
57
|
private void setDateFormat(ReadableMap mapStyle) {
|
|
56
|
-
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
String dateFormat = mapStyle.getString(DATE_FORMAT_KEY);
|
|
58
|
+
String dateFormat = getString(mapStyle, DATE_FORMAT_KEY);
|
|
60
59
|
if (dateFormat != null && !dateFormat.isEmpty()) {
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
try {
|
|
61
|
+
PushwooshInboxStyle.INSTANCE.setDateFormatter(new ReactInboxDateFormatter(dateFormat));
|
|
62
|
+
} catch (IllegalArgumentException e) {
|
|
63
|
+
PWLog.error(PushwooshPlugin.TAG, "Invalid inbox dateFormat pattern: " + dateFormat, e);
|
|
64
|
+
}
|
|
63
65
|
}
|
|
64
66
|
}
|
|
65
67
|
|
|
66
68
|
private void setTexts(ReadableMap mapStyle) {
|
|
67
69
|
PushwooshInboxStyle PWInboxStyle = PushwooshInboxStyle.INSTANCE;
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
String listErrorMessage = getString(mapStyle, LIST_ERROR_MESSAGE_KEY);
|
|
71
|
+
if (listErrorMessage != null)
|
|
72
|
+
PWInboxStyle.setListErrorMessage(listErrorMessage);
|
|
73
|
+
String listEmptyMessage = getString(mapStyle, LIST_EMPTY_MESSAGE_KEY);
|
|
74
|
+
if (listEmptyMessage != null)
|
|
75
|
+
PWInboxStyle.setListEmptyText(listEmptyMessage);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// A production ReadableNativeMap throws out of presentInboxUI() on a typed read of a
|
|
79
|
+
// mismatched or null value, so the type is checked before the value is taken.
|
|
80
|
+
private boolean hasTypedValue(ReadableMap mapStyle, String key, ReadableType type, String label) {
|
|
81
|
+
if (!mapStyle.hasKey(key)) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
if (mapStyle.getType(key) != type) {
|
|
85
|
+
PWLog.error(PushwooshPlugin.TAG, "Inbox style " + key + " must be " + label + "; ignored.");
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private String getString(ReadableMap mapStyle, String key) {
|
|
92
|
+
return hasTypedValue(mapStyle, key, ReadableType.String, "a string") ? mapStyle.getString(key) : null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private Integer getColor(ReadableMap mapStyle, String key) {
|
|
96
|
+
return hasTypedValue(mapStyle, key, ReadableType.Number, "a number") ? mapStyle.getInt(key) : null;
|
|
72
97
|
}
|
|
73
98
|
|
|
74
99
|
private void setImages(ReadableMap mapStyle) {
|
|
@@ -87,11 +112,15 @@ class InboxUiStyleManager {
|
|
|
87
112
|
}
|
|
88
113
|
|
|
89
114
|
private Drawable getImage(ReadableMap mapStyle, String key) {
|
|
90
|
-
if (!mapStyle.
|
|
115
|
+
if (!hasTypedValue(mapStyle, key, ReadableType.Map, "an object with a uri")) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
ReadableMap imageMap = mapStyle.getMap(key);
|
|
119
|
+
if (!imageMap.hasKey(URI_KEY) || imageMap.getType(URI_KEY) != ReadableType.String) {
|
|
120
|
+
PWLog.error(PushwooshPlugin.TAG, "Inbox style " + key + "." + URI_KEY + " must be a string; ignored.");
|
|
91
121
|
return null;
|
|
92
122
|
}
|
|
93
|
-
|
|
94
|
-
String uri = defaultImageIcon.getString(URI_KEY);
|
|
123
|
+
String uri = imageMap.getString(URI_KEY);
|
|
95
124
|
try {
|
|
96
125
|
return getDrawable(uri);
|
|
97
126
|
} catch (IOException e) {
|
|
@@ -113,36 +142,49 @@ class InboxUiStyleManager {
|
|
|
113
142
|
private void setColors(ReadableMap mapStyle) {
|
|
114
143
|
PushwooshInboxStyle PWInboxStyle = PushwooshInboxStyle.INSTANCE;
|
|
115
144
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if (
|
|
121
|
-
PWInboxStyle.
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
if (
|
|
131
|
-
PWInboxStyle.
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
if (
|
|
138
|
-
PWInboxStyle.
|
|
139
|
-
|
|
140
|
-
if (
|
|
141
|
-
PWInboxStyle.
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
if (
|
|
145
|
-
PWInboxStyle.
|
|
145
|
+
Integer accentColor = getColor(mapStyle, ACCENT_COLOR_KEY);
|
|
146
|
+
if (accentColor != null)
|
|
147
|
+
PWInboxStyle.setAccentColor(accentColor);
|
|
148
|
+
Integer highlightColor = getColor(mapStyle, HIGHLIGHT_COLOR_KEY);
|
|
149
|
+
if (highlightColor != null)
|
|
150
|
+
PWInboxStyle.setHighlightColor(highlightColor);
|
|
151
|
+
Integer backgroundColor = getColor(mapStyle, BACKGROUND_COLOR_KEY);
|
|
152
|
+
if (backgroundColor != null)
|
|
153
|
+
PWInboxStyle.setBackgroundColor(backgroundColor);
|
|
154
|
+
Integer dividerColor = getColor(mapStyle, DIVIDER_COLOR_KEY);
|
|
155
|
+
if (dividerColor != null)
|
|
156
|
+
PWInboxStyle.setDividerColor(dividerColor);
|
|
157
|
+
|
|
158
|
+
Integer dateColor = getColor(mapStyle, DATE_COLOR_KEY);
|
|
159
|
+
if (dateColor != null)
|
|
160
|
+
PWInboxStyle.setDateColor(dateColor);
|
|
161
|
+
Integer readDateColor = getColor(mapStyle, READ_DATE_COLOR_KEY);
|
|
162
|
+
if (readDateColor != null)
|
|
163
|
+
PWInboxStyle.setReadDateColor(readDateColor);
|
|
164
|
+
|
|
165
|
+
Integer titleColor = getColor(mapStyle, TITLE_COLOR_KEY);
|
|
166
|
+
if (titleColor != null)
|
|
167
|
+
PWInboxStyle.setTitleColor(titleColor);
|
|
168
|
+
Integer readTitleColor = getColor(mapStyle, READ_TITLE_COLOR_KEY);
|
|
169
|
+
if (readTitleColor != null)
|
|
170
|
+
PWInboxStyle.setReadTitleColor(readTitleColor);
|
|
171
|
+
|
|
172
|
+
Integer descriptionColor = getColor(mapStyle, DESCRIPTION_COLOR_KEY);
|
|
173
|
+
if (descriptionColor != null)
|
|
174
|
+
PWInboxStyle.setDescriptionColor(descriptionColor);
|
|
175
|
+
Integer readDescriptionColor = getColor(mapStyle, READ_DESCRIPTION_COLOR_KEY);
|
|
176
|
+
if (readDescriptionColor != null)
|
|
177
|
+
PWInboxStyle.setReadDescriptionColor(readDescriptionColor);
|
|
178
|
+
|
|
179
|
+
Integer barBackgroundColor = getColor(mapStyle, BAR_BACKGROUND_COLOR);
|
|
180
|
+
if (barBackgroundColor != null)
|
|
181
|
+
PWInboxStyle.setBarBackgroundColor(barBackgroundColor);
|
|
182
|
+
Integer barAccentColor = getColor(mapStyle, BAR_ACCENT_COLOR);
|
|
183
|
+
if (barAccentColor != null)
|
|
184
|
+
PWInboxStyle.setBarAccentColor(barAccentColor);
|
|
185
|
+
Integer barTextColor = getColor(mapStyle, BAR_TEXT_COLOR);
|
|
186
|
+
if (barTextColor != null)
|
|
187
|
+
PWInboxStyle.setBarTextColor(barTextColor);
|
|
146
188
|
}
|
|
147
189
|
|
|
148
190
|
private class ReactInboxDateFormatter implements InboxDateFormatter {
|
|
@@ -130,7 +130,7 @@ public class PushwooshPlugin extends PushwooshPluginSpec implements LifecycleEve
|
|
|
130
130
|
|
|
131
131
|
@ReactMethod
|
|
132
132
|
public void init(ReadableMap config, Callback success, Callback error) {
|
|
133
|
-
String appId = config.hasKey("pw_appid") ? config.getString("pw_appid") : null;
|
|
133
|
+
String appId = config != null && config.hasKey("pw_appid") ? config.getString("pw_appid") : null;
|
|
134
134
|
|
|
135
135
|
if (appId == null) {
|
|
136
136
|
if (error != null) {
|
|
@@ -441,12 +441,11 @@ public class PushwooshPlugin extends PushwooshPluginSpec implements LifecycleEve
|
|
|
441
441
|
}
|
|
442
442
|
|
|
443
443
|
Activity currentActivity = getCurrentActivity();
|
|
444
|
-
|
|
445
|
-
if (currentActivity != null) {
|
|
446
|
-
currentActivity.startActivity(intent);
|
|
447
|
-
}else {
|
|
444
|
+
if (currentActivity == null) {
|
|
448
445
|
PWLog.error(TAG, "current activity is null");
|
|
446
|
+
return;
|
|
449
447
|
}
|
|
448
|
+
currentActivity.startActivity(new Intent(currentActivity, InboxActivity.class));
|
|
450
449
|
}
|
|
451
450
|
|
|
452
451
|
@ReactMethod
|
|
@@ -564,6 +563,10 @@ public class PushwooshPlugin extends PushwooshPluginSpec implements LifecycleEve
|
|
|
564
563
|
|
|
565
564
|
@ReactMethod
|
|
566
565
|
public void setNotificationIconBackgroundColor(String color) {
|
|
566
|
+
if (color == null || color.isEmpty()) {
|
|
567
|
+
PWLog.error(TAG, "setNotificationIconBackgroundColor: color is required");
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
567
570
|
int intColor;
|
|
568
571
|
try {
|
|
569
572
|
intColor = Color.parseColor(color);
|
|
@@ -3,11 +3,18 @@ package com.pushwoosh.reactnativeplugin.internal;
|
|
|
3
3
|
import com.pushwoosh.internal.PluginProvider;
|
|
4
4
|
|
|
5
5
|
public class ReactNativePluginProvider implements PluginProvider {
|
|
6
|
+
private static final String PLUGIN_VERSION = "7.0.3";
|
|
7
|
+
|
|
6
8
|
@Override
|
|
7
9
|
public String getPluginType() {
|
|
8
10
|
return "React Native";
|
|
9
11
|
}
|
|
10
12
|
|
|
13
|
+
// No @Override: getPluginVersion() lands in PluginProvider only in the next Android SDK release.
|
|
14
|
+
public String getPluginVersion() {
|
|
15
|
+
return PLUGIN_VERSION;
|
|
16
|
+
}
|
|
17
|
+
|
|
11
18
|
@Override
|
|
12
19
|
public int richMediaStartDelay() {
|
|
13
20
|
return DEFAULT_RICH_MEDIA_START_DELAY;
|
|
@@ -72,6 +72,17 @@ static NSDictionary * gStartPushData = nil;
|
|
|
72
72
|
static BOOL gStartPushReplayed = NO; // Launch push already replayed into the JS events by init:
|
|
73
73
|
static NSURL * gPushDeepLinkURL = nil; // Deep link URL for New Architecture support
|
|
74
74
|
static NSString * gEarlyHandledHash = nil; // Last push hash the cold-start delegate accepted, to avoid double handling
|
|
75
|
+
static NSString * gPendingWebLaunchLink = nil; // http(s) link of the launch push, until the SDK hands it to RCTLinkingManager
|
|
76
|
+
static NSUInteger gPendingWebLaunchLinkGeneration = 0; // Bumped on every arm, so a stale delivery timeout leaves a newer link alone
|
|
77
|
+
static NSString * gLastArmedWebLaunchLink = nil; // Survives delivery, so re-arming the same push is recognised
|
|
78
|
+
static BOOL gInitialURLAnswered = NO; // JS called getInitialURL() since gLastArmedWebLaunchLink was first armed
|
|
79
|
+
static BOOL gPluginInitialized = NO; // init: ran, so JS is up and a tapped push is no longer a launch push
|
|
80
|
+
static NSMutableArray<void (^)(NSString *)> * gInitialURLWaiters = nil; // getInitialURL() calls held for the pending link
|
|
81
|
+
static NSUInteger gInitialURLHoldGeneration = 0; // Bumped on every settle, so a stale hold timeout leaves a newer hold alone
|
|
82
|
+
// The SDK waits 0.2 s after accepting the push, then fetches apple-app-site-association with a 5 s timeout.
|
|
83
|
+
static NSTimeInterval const kPWWebLaunchLinkDeliveryTimeout = 6.0;
|
|
84
|
+
// Covers the SDK's delay and a cached or quick verdict without stalling an app whose link never reaches RN.
|
|
85
|
+
static NSTimeInterval const kPWInitialURLHoldTimeout = 1.0;
|
|
75
86
|
static NSString * const kRegistrationSuccesEvent = @"PWRegistrationSuccess";
|
|
76
87
|
static NSString * const kRegistrationErrorEvent = @"PWRegistrationError";
|
|
77
88
|
static NSString * const kPushReceivedEvent = @"PWPushReceived";
|
|
@@ -80,6 +91,25 @@ static NSString * const kPushOpenEvent = @"PWPushOpen";
|
|
|
80
91
|
static NSString * const kPushOpenJSEvent = @"pushOpened";
|
|
81
92
|
static NSString * const kPushReceivedJSEvent = @"pushReceived";
|
|
82
93
|
|
|
94
|
+
static NSString * const kPWFrameworkType = @"React Native";
|
|
95
|
+
static NSString * const kPWFrameworkVersion = @"7.0.3";
|
|
96
|
+
|
|
97
|
+
/// Reports the framework type and the plugin version to the native core, on the cores that take them.
|
|
98
|
+
/// Dispatched by selector name: setFrameworkType:version: lands in a core newer than the pinned one.
|
|
99
|
+
static void pwplugin_setFrameworkTelemetry(NSString *type, NSString *version) {
|
|
100
|
+
Class configuration = [Pushwoosh configure];
|
|
101
|
+
SEL selector = NSSelectorFromString(@"setFrameworkType:version:");
|
|
102
|
+
|
|
103
|
+
if (![configuration respondsToSelector:selector]) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
#pragma clang diagnostic push
|
|
108
|
+
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
|
|
109
|
+
[configuration performSelector:selector withObject:type withObject:version];
|
|
110
|
+
#pragma clang diagnostic pop
|
|
111
|
+
}
|
|
112
|
+
|
|
83
113
|
/// Returns the push link only when it is a deep link this app can route itself, otherwise nil.
|
|
84
114
|
///
|
|
85
115
|
/// http/https links are left to the native SDK: it checks the host's apple-app-site-association
|
|
@@ -101,6 +131,78 @@ static NSURL * pwplugin_deepLinkURLFromPushLink(id link) {
|
|
|
101
131
|
return url;
|
|
102
132
|
}
|
|
103
133
|
|
|
134
|
+
/// Returns the http(s) push link as `URL.absoluteString`, the form RCTLinkingManager posts, otherwise nil.
|
|
135
|
+
static NSString * pwplugin_webLinkFromPushLink(id link) {
|
|
136
|
+
if (![link isKindOfClass:[NSString class]] || [link length] == 0) {
|
|
137
|
+
return nil;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
NSURL *url = [NSURL URLWithString:link];
|
|
141
|
+
NSString *scheme = url.scheme.lowercaseString;
|
|
142
|
+
if ([scheme isEqualToString:@"http"] || [scheme isEqualToString:@"https"]) {
|
|
143
|
+
return url.absoluteString;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return nil;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/// Answers every getInitialURL() call held for the pending link: with `url` when it arrived, otherwise
|
|
150
|
+
/// with what RCTLinkingManager itself reports. JS has read its initial URL either way.
|
|
151
|
+
static void pwplugin_settleInitialURLWaiters(NSString *url) {
|
|
152
|
+
NSArray<void (^)(NSString *)> *waiters = gInitialURLWaiters;
|
|
153
|
+
gInitialURLWaiters = nil;
|
|
154
|
+
gInitialURLHoldGeneration++;
|
|
155
|
+
gInitialURLAnswered = YES;
|
|
156
|
+
for (void (^waiter)(NSString *) in waiters) {
|
|
157
|
+
waiter(url);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/// Arms the launch push's web link for the SDK's delivery window; called by every launch-push site.
|
|
162
|
+
/// A push without a web link disarms it, so a later Safari open of the same URL is not caught.
|
|
163
|
+
static void pwplugin_rememberWebLaunchLink(NSDictionary *push) {
|
|
164
|
+
NSString *link = pwplugin_webLinkFromPushLink(push[@"l"]);
|
|
165
|
+
// The SDK accepts the same launch push at init() again, possibly after JS read getInitialURL().
|
|
166
|
+
if (!link || ![link isEqualToString:gLastArmedWebLaunchLink]) {
|
|
167
|
+
gInitialURLAnswered = NO;
|
|
168
|
+
}
|
|
169
|
+
gLastArmedWebLaunchLink = link;
|
|
170
|
+
gPendingWebLaunchLink = link;
|
|
171
|
+
NSUInteger generation = ++gPendingWebLaunchLinkGeneration;
|
|
172
|
+
|
|
173
|
+
if (!link) {
|
|
174
|
+
if (gInitialURLWaiters) {
|
|
175
|
+
pwplugin_settleInitialURLWaiters(nil);
|
|
176
|
+
}
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kPWWebLaunchLinkDeliveryTimeout * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
181
|
+
if (generation == gPendingWebLaunchLinkGeneration) {
|
|
182
|
+
gPendingWebLaunchLink = nil;
|
|
183
|
+
if (gInitialURLWaiters) {
|
|
184
|
+
pwplugin_settleInitialURLWaiters(nil);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/// Catches the launch push's Universal Link on its way to RCTLinkingManager: resolves held getInitialURL()
|
|
191
|
+
/// calls, or parks it for the first one; a link after getInitialURL() answered is left to the `url` event.
|
|
192
|
+
static void pwplugin_handleOpenURLNotification(NSNotification *notification) {
|
|
193
|
+
id url = notification.userInfo[@"url"];
|
|
194
|
+
if (!gPendingWebLaunchLink || ![url isKindOfClass:[NSString class]] || ![url isEqualToString:gPendingWebLaunchLink]) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
gPendingWebLaunchLink = nil;
|
|
199
|
+
if (gInitialURLWaiters) {
|
|
200
|
+
pwplugin_settleInitialURLWaiters(url);
|
|
201
|
+
} else if (!gInitialURLAnswered) {
|
|
202
|
+
gPushDeepLinkURL = [NSURL URLWithString:url];
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
104
206
|
// Bridgeless hands a module an RCTBridgeProxy: an NSProxy that carries none of NSObject's KVC and
|
|
105
207
|
// turns an unknown selector into a logged no-op. object_getClass asks without messaging it.
|
|
106
208
|
static BOOL pwplugin_isRealBridge(id bridge) {
|
|
@@ -124,25 +226,50 @@ static BOOL pwplugin_isRealBridge(id bridge) {
|
|
|
124
226
|
- (void)pwplugin_original_getInitialURL:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject;
|
|
125
227
|
@end
|
|
126
228
|
|
|
229
|
+
static void pwplugin_originalGetInitialURL(id self, RCTPromiseResolveBlock resolve, RCTPromiseRejectBlock reject) {
|
|
230
|
+
SEL originalSelector = @selector(pwplugin_original_getInitialURL:reject:);
|
|
231
|
+
if ([self respondsToSelector:originalSelector]) {
|
|
232
|
+
void (*originalIMP)(id, SEL, RCTPromiseResolveBlock, RCTPromiseRejectBlock);
|
|
233
|
+
originalIMP = (void (*)(id, SEL, RCTPromiseResolveBlock, RCTPromiseRejectBlock))objc_msgSend;
|
|
234
|
+
originalIMP(self, originalSelector, resolve, reject);
|
|
235
|
+
} else {
|
|
236
|
+
resolve([NSNull null]);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
127
240
|
// Swizzled getInitialURL for RCTLinkingManager (New Architecture support)
|
|
128
241
|
static void pwplugin_getInitialURL(id self, SEL _cmd, RCTPromiseResolveBlock resolve, RCTPromiseRejectBlock reject) {
|
|
129
242
|
// If we have a saved deep link URL from push notification, return it
|
|
130
243
|
if (gPushDeepLinkURL) {
|
|
131
244
|
NSURL *url = gPushDeepLinkURL;
|
|
132
245
|
gPushDeepLinkURL = nil; // Clear after use (one-time)
|
|
246
|
+
gInitialURLAnswered = YES;
|
|
133
247
|
resolve(url.absoluteString);
|
|
134
248
|
return;
|
|
135
249
|
}
|
|
136
250
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
251
|
+
if (gPendingWebLaunchLink && !gInitialURLAnswered) {
|
|
252
|
+
if (!gInitialURLWaiters) {
|
|
253
|
+
gInitialURLWaiters = [NSMutableArray array];
|
|
254
|
+
NSUInteger hold = gInitialURLHoldGeneration;
|
|
255
|
+
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kPWInitialURLHoldTimeout * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
256
|
+
if (hold == gInitialURLHoldGeneration) {
|
|
257
|
+
pwplugin_settleInitialURLWaiters(nil);
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
[gInitialURLWaiters addObject:[^(NSString *url) {
|
|
262
|
+
if (url) {
|
|
263
|
+
resolve(url);
|
|
264
|
+
} else {
|
|
265
|
+
pwplugin_originalGetInitialURL(self, resolve, reject);
|
|
266
|
+
}
|
|
267
|
+
} copy]];
|
|
268
|
+
return;
|
|
145
269
|
}
|
|
270
|
+
|
|
271
|
+
gInitialURLAnswered = YES;
|
|
272
|
+
pwplugin_originalGetInitialURL(self, resolve, reject);
|
|
146
273
|
}
|
|
147
274
|
|
|
148
275
|
void pushwoosh_swizzle(Class cls, SEL fromChange, SEL toChange, IMP impl, const char * signature) {
|
|
@@ -210,7 +337,9 @@ RCT_EXPORT_METHOD(init:(NSDictionary*)config success:(RCTResponseSenderBlock)suc
|
|
|
210
337
|
return;
|
|
211
338
|
}
|
|
212
339
|
|
|
340
|
+
gPluginInitialized = YES;
|
|
213
341
|
[PushNotificationManager initializeWithAppCode:appCode appName:nil];
|
|
342
|
+
pwplugin_setFrameworkTelemetry(kPWFrameworkType, kPWFrameworkVersion);
|
|
214
343
|
[[PushNotificationManager pushManager] sendAppOpen];
|
|
215
344
|
[PushNotificationManager pushManager].delegate = self;
|
|
216
345
|
|
|
@@ -1118,6 +1247,7 @@ RCT_EXPORT_METHOD(getRichMediaType:(RCTResponseSenderBlock)callback) {
|
|
|
1118
1247
|
if (onStart) {
|
|
1119
1248
|
gStartPushData = pushNotification;
|
|
1120
1249
|
gStartPushReplayed = NO;
|
|
1250
|
+
pwplugin_rememberWebLaunchLink(pushNotification);
|
|
1121
1251
|
// Save deep link URL for New Architecture (Linking.getInitialURL support)
|
|
1122
1252
|
NSURL *deepLink = pwplugin_deepLinkURLFromPushLink(pushNotification[@"l"]);
|
|
1123
1253
|
if (deepLink) {
|
|
@@ -1129,6 +1259,7 @@ RCT_EXPORT_METHOD(getRichMediaType:(RCTResponseSenderBlock)callback) {
|
|
|
1129
1259
|
if (onStart) {
|
|
1130
1260
|
gStartPushData = pushNotification;
|
|
1131
1261
|
gStartPushReplayed = NO;
|
|
1262
|
+
pwplugin_rememberWebLaunchLink(pushNotification);
|
|
1132
1263
|
// Save deep link URL for New Architecture (Linking.getInitialURL support)
|
|
1133
1264
|
NSURL *deepLink = pwplugin_deepLinkURLFromPushLink(pushNotification[@"l"]);
|
|
1134
1265
|
if (deepLink) {
|
|
@@ -1181,9 +1312,12 @@ static void pwplugin_acceptColdStartPushOnce(NSDictionary *userInfo) {
|
|
|
1181
1312
|
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
|
|
1182
1313
|
didReceiveNotificationResponse:(UNNotificationResponse *)response
|
|
1183
1314
|
withCompletionHandler:(void (^)(void))completionHandler API_AVAILABLE(ios(10.0)) {
|
|
1184
|
-
|
|
1315
|
+
BOOL dismissed = [response.actionIdentifier isEqualToString:UNNotificationDismissActionIdentifier];
|
|
1316
|
+
// After init the plugin's own delegate or the app accepts the tap; a dismissed push opened nothing.
|
|
1317
|
+
if (!gPluginInitialized && !dismissed && [response.notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
|
|
1185
1318
|
NSDictionary *userInfo = response.notification.request.content.userInfo;
|
|
1186
1319
|
NSURL *appSchemeDeepLink = pwplugin_deepLinkURLFromPushLink(userInfo[@"l"]);
|
|
1320
|
+
pwplugin_rememberWebLaunchLink(userInfo);
|
|
1187
1321
|
|
|
1188
1322
|
if (appSchemeDeepLink) {
|
|
1189
1323
|
// Routing it through the SDK too would deliver the deep link twice.
|
|
@@ -1216,6 +1350,19 @@ didReceiveNotificationResponse:(UNNotificationResponse *)response
|
|
|
1216
1350
|
|
|
1217
1351
|
static PWEarlyNotificationDelegate *gEarlyDelegate = nil;
|
|
1218
1352
|
|
|
1353
|
+
// Wraps the delegate the app set in didFinishLaunching. A scene-based app gets the launch push's
|
|
1354
|
+
// response before the main queue drains, so installing it any later loses the push.
|
|
1355
|
+
static void pwplugin_installEarlyNotificationDelegate(void) {
|
|
1356
|
+
if (gEarlyDelegate) {
|
|
1357
|
+
return;
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
|
|
1361
|
+
gEarlyDelegate = [[PWEarlyNotificationDelegate alloc] init];
|
|
1362
|
+
gEarlyDelegate.originalDelegate = center.delegate;
|
|
1363
|
+
center.delegate = gEarlyDelegate;
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1219
1366
|
@implementation RCTLinkingManager (PushwooshDeepLink)
|
|
1220
1367
|
|
|
1221
1368
|
+ (void)load {
|
|
@@ -1230,25 +1377,25 @@ static PWEarlyNotificationDelegate *gEarlyDelegate = nil;
|
|
|
1230
1377
|
"v@:@@"
|
|
1231
1378
|
);
|
|
1232
1379
|
|
|
1233
|
-
//
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
});
|
|
1241
|
-
}
|
|
1380
|
+
// RN keeps the notification name file-local; the string is its contract with the app delegate.
|
|
1381
|
+
[[NSNotificationCenter defaultCenter] addObserverForName:@"RCTOpenURLNotification"
|
|
1382
|
+
object:nil
|
|
1383
|
+
queue:nil
|
|
1384
|
+
usingBlock:^(NSNotification *notification) {
|
|
1385
|
+
pwplugin_handleOpenURLNotification(notification);
|
|
1386
|
+
}];
|
|
1242
1387
|
|
|
1243
|
-
// Also check launchOptions when app finishes launching
|
|
1244
1388
|
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidFinishLaunchingNotification
|
|
1245
1389
|
object:nil
|
|
1246
1390
|
queue:nil
|
|
1247
1391
|
usingBlock:^(NSNotification *notification) {
|
|
1392
|
+
pwplugin_installEarlyNotificationDelegate();
|
|
1393
|
+
|
|
1248
1394
|
NSDictionary *launchOptions = notification.userInfo;
|
|
1249
1395
|
NSDictionary *remoteNotification = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
|
|
1250
1396
|
if (remoteNotification) {
|
|
1251
1397
|
NSURL *deepLink = pwplugin_deepLinkURLFromPushLink(remoteNotification[@"l"]);
|
|
1398
|
+
pwplugin_rememberWebLaunchLink(remoteNotification);
|
|
1252
1399
|
if (deepLink) {
|
|
1253
1400
|
if (!gPushDeepLinkURL) {
|
|
1254
1401
|
gPushDeepLinkURL = deepLink;
|