pushwoosh-react-native-plugin 6.1.61 → 7.0.1
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/README.md +25 -1
- package/index.d.ts +4 -0
- package/index.js +5 -3
- package/package.json +12 -1
- package/pushwoosh-react-native-plugin.podspec +11 -7
- package/src/android/build.gradle +115 -3
- package/src/android/proguard-rules.pro +16 -0
- package/src/android/src/main/java/com/pushwoosh/reactnativeplugin/ConversionUtil.java +3 -4
- package/src/android/src/main/java/com/pushwoosh/reactnativeplugin/EventDispatcher.java +10 -1
- package/src/android/src/main/java/com/pushwoosh/reactnativeplugin/PushwooshPackage.java +32 -19
- package/src/android/src/main/java/com/pushwoosh/reactnativeplugin/PushwooshPlugin.java +161 -65
- package/src/android/src/newarch/java/com/pushwoosh/reactnativeplugin/PushwooshPluginSpec.java +10 -0
- package/src/android/src/oldarch/java/com/pushwoosh/reactnativeplugin/PushwooshPluginSpec.java +11 -0
- package/src/ios/PushwooshPlugin/Pushwoosh.h +6 -0
- package/src/ios/PushwooshPlugin/{Pushwoosh.m → Pushwoosh.mm} +120 -53
- package/src/package.json +11 -0
- package/src/specs/NativePushwoosh.ts +73 -0
package/README.md
CHANGED
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
|
|
26
26
|
- [Integration Guide](https://docs.pushwoosh.com/platform-docs/pushwoosh-sdk/cross-platform-frameworks/react-native/integrating-react-native-plugin) — step-by-step setup
|
|
27
27
|
- [API Reference](docs/README.md) — full API documentation
|
|
28
|
-
- [Sample Project](https://github.com/Pushwoosh/pushwoosh-react-native-plugin/tree/master/example/demoapp) — ready-to-run demo app
|
|
28
|
+
- [Sample Project](https://github.com/Pushwoosh/pushwoosh-react-native-plugin/tree/master/example/demoapp) — ready-to-run demo app on the current React Native (New Architecture)
|
|
29
|
+
- [Legacy Sample Project](https://github.com/Pushwoosh/pushwoosh-react-native-plugin/tree/master/example/demoapp-legacy) — the same demo on React Native 0.74 with the legacy architecture
|
|
29
30
|
|
|
30
31
|
## Features
|
|
31
32
|
|
|
@@ -76,6 +77,29 @@ The wizard will automatically add all required configuration.
|
|
|
76
77
|
|
|
77
78
|
The Pushwoosh plugin already includes the `firebase-messaging` dependency, so you do not need to add it manually.
|
|
78
79
|
|
|
80
|
+
### React Native versions and the New Architecture
|
|
81
|
+
|
|
82
|
+
The plugin is a Turbo Native Module and also works on the legacy bridge. One package covers both:
|
|
83
|
+
|
|
84
|
+
| React Native | Architecture | How the plugin runs |
|
|
85
|
+
|---|---|---|
|
|
86
|
+
| 0.74 – 0.81, `newArchEnabled=false` (default) | Legacy bridge | Bridge module |
|
|
87
|
+
| 0.74 – 0.81, `newArchEnabled=true` / `RCT_NEW_ARCH_ENABLED=1` | New Architecture | Codegen TurboModule |
|
|
88
|
+
| 0.82 and later | New Architecture only (bridgeless) | Codegen TurboModule |
|
|
89
|
+
|
|
90
|
+
No interop layer is involved on the New Architecture. Events keep their API on both architectures:
|
|
91
|
+
|
|
92
|
+
```javascript
|
|
93
|
+
import { DeviceEventEmitter } from 'react-native';
|
|
94
|
+
|
|
95
|
+
DeviceEventEmitter.addListener('pushOpened', (push) => { /* ... */ });
|
|
96
|
+
DeviceEventEmitter.addListener('pushReceived', (push) => { /* ... */ });
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`presentInboxUI()` presents a native screen (a view controller on iOS, an Activity on Android) outside the React view tree, so it does not depend on Fabric. React Native below 0.74 is supported by plugin versions 6.1.x.
|
|
100
|
+
|
|
101
|
+
Both setups ship as samples with identical screens: `example/demoapp` (React Native 0.87, New Architecture) and `example/demoapp-legacy` (React Native 0.74, legacy architecture).
|
|
102
|
+
|
|
79
103
|
## AI-Assisted Integration
|
|
80
104
|
|
|
81
105
|
Integrate the Pushwoosh React Native plugin using AI coding assistants (Claude Code, Cursor, GitHub Copilot, etc.).
|
package/index.d.ts
CHANGED
|
@@ -87,6 +87,10 @@ declare module 'pushwoosh-react-native-plugin' {
|
|
|
87
87
|
isCommunicationEnabled(success: (isEnabled: boolean)=> void): void;
|
|
88
88
|
setCommunicationEnabled(enabled: boolean, success?: () => void, fail?: (error: Error) => void): void;
|
|
89
89
|
|
|
90
|
+
//Rich Media presentation style
|
|
91
|
+
setRichMediaType(type: number): void;
|
|
92
|
+
getRichMediaType(callback: (type: number) => void): void;
|
|
93
|
+
RichMediaStyle: { MODAL: number; LEGACY: number };
|
|
90
94
|
}
|
|
91
95
|
|
|
92
96
|
const Pushwoosh: Pushwoosh;
|
package/index.js
CHANGED
|
@@ -201,9 +201,11 @@
|
|
|
201
201
|
* @module pushwoosh-react-native-plugin
|
|
202
202
|
*/
|
|
203
203
|
|
|
204
|
-
import
|
|
204
|
+
import NativePushwoosh from './src/specs/NativePushwoosh';
|
|
205
205
|
|
|
206
|
-
|
|
206
|
+
// Resolved through TurboModuleRegistry: the codegen TurboModule on the New Architecture, the
|
|
207
|
+
// bridge module on the legacy architecture.
|
|
208
|
+
const PushwooshModule = NativePushwoosh;
|
|
207
209
|
|
|
208
210
|
//Constant: RichMediaStyle
|
|
209
211
|
//Rich Media presentation style constants.
|
|
@@ -348,7 +350,7 @@ class PushNotification {
|
|
|
348
350
|
if (!fail) {
|
|
349
351
|
fail = function(error) {};
|
|
350
352
|
}
|
|
351
|
-
PushwooshModule.
|
|
353
|
+
PushwooshModule.registerForPushNotifications(success, fail);
|
|
352
354
|
}
|
|
353
355
|
|
|
354
356
|
//Function: unregister
|
package/package.json
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pushwoosh-react-native-plugin",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.1",
|
|
4
4
|
"description": "This plugin allows you to send and receive push notifications. Powered by Pushwoosh (www.pushwoosh.com).",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
7
|
+
"codegenConfig": {
|
|
8
|
+
"name": "RNPushwooshSpec",
|
|
9
|
+
"type": "modules",
|
|
10
|
+
"jsSrcsDir": "src/specs",
|
|
11
|
+
"android": {
|
|
12
|
+
"javaPackageName": "com.pushwoosh.reactnativeplugin"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
7
15
|
"repository": {
|
|
8
16
|
"type": "git",
|
|
9
17
|
"url": "git+https://github.com/Pushwoosh/pushwoosh-react-native-plugin.git"
|
|
@@ -17,6 +25,9 @@
|
|
|
17
25
|
"bugs": {
|
|
18
26
|
"url": "https://github.com/Pushwoosh/pushwoosh-react-native-plugin/issues"
|
|
19
27
|
},
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"react-native": ">=0.74.0"
|
|
30
|
+
},
|
|
20
31
|
"rnpm": {
|
|
21
32
|
"android": {
|
|
22
33
|
"sourceDir": "./src/android"
|
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
Pod::Spec.new do |s|
|
|
2
2
|
s.name = "pushwoosh-react-native-plugin"
|
|
3
|
-
s.version = "
|
|
3
|
+
s.version = "7.0.1"
|
|
4
4
|
s.summary = "React Native Pushwoosh Push Notifications module"
|
|
5
5
|
s.requires_arc = true
|
|
6
6
|
s.author = 'Pushwoosh'
|
|
7
7
|
s.license = 'MIT'
|
|
8
8
|
s.homepage = 'n/a'
|
|
9
9
|
s.source = { :git => "https://github.com/Pushwoosh/pushwoosh-react-native-plugin.git" }
|
|
10
|
-
s.source_files = 'src/ios/PushwooshPlugin/Pushwoosh.{h,
|
|
10
|
+
s.source_files = 'src/ios/PushwooshPlugin/Pushwoosh.{h,mm}', 'src/ios/PushwooshPlugin/PWEventDispatcher.{h,m}'
|
|
11
11
|
s.platform = :ios, "13.0"
|
|
12
|
-
s.xcconfig = {
|
|
13
|
-
"HEADER_SEARCH_PATHS" => "${PODS_ROOT}/Headers/Public/React"
|
|
14
|
-
}
|
|
15
12
|
s.static_framework = true
|
|
16
13
|
|
|
17
|
-
|
|
14
|
+
# React Native 0.71+ adds React-Core and, when the New Architecture is on, the codegen and
|
|
15
|
+
# TurboModule dependencies together with the RCT_NEW_ARCH_ENABLED define.
|
|
16
|
+
if respond_to?(:install_modules_dependencies, true)
|
|
17
|
+
install_modules_dependencies(s)
|
|
18
|
+
else
|
|
19
|
+
s.dependency "React-Core"
|
|
20
|
+
end
|
|
21
|
+
|
|
18
22
|
s.dependency 'PushwooshXCFramework', '7.2.6'
|
|
19
|
-
s.dependency 'PushwooshInboxUIXCFramework'
|
|
23
|
+
s.dependency 'PushwooshInboxUIXCFramework', '7.0.42'
|
|
20
24
|
end
|
package/src/android/build.gradle
CHANGED
|
@@ -20,28 +20,135 @@ allprojects {
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
// [major, minor] of the react-native the app resolves, or null when it cannot be found.
|
|
24
|
+
// Every node_modules above the android project is a candidate: the template keeps react-native in
|
|
25
|
+
// the one next door, a monorepo hoists it any number of levels up. The included build of the React
|
|
26
|
+
// Native Gradle Plugin names the node_modules the app really resolves, though only a 0.74-style
|
|
27
|
+
// settings.gradle exposes it - from 0.82 the include sits in pluginManagement and stays private.
|
|
28
|
+
// `node --print` answers the way Metro does and comes last: it needs node on the Gradle PATH,
|
|
29
|
+
// which an IDE launched from the Finder does not have.
|
|
30
|
+
def reactNativeVersion() {
|
|
31
|
+
def candidates = []
|
|
32
|
+
for (def dir = rootProject.projectDir; dir != null; dir = dir.parentFile) {
|
|
33
|
+
candidates += new File(dir, "node_modules/react-native/package.json")
|
|
34
|
+
}
|
|
35
|
+
project.gradle.includedBuilds.each { included ->
|
|
36
|
+
candidates += new File(included.projectDir, "../../react-native/package.json")
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
def resolved = ["node", "--print", "require.resolve('react-native/package.json')"]
|
|
40
|
+
.execute(null, rootProject.projectDir).text.trim()
|
|
41
|
+
if (!resolved.isEmpty()) {
|
|
42
|
+
candidates += new File(resolved)
|
|
43
|
+
}
|
|
44
|
+
} catch (Exception ignored) {
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
for (candidate in candidates) {
|
|
48
|
+
if (!candidate.isFile()) {
|
|
49
|
+
continue
|
|
50
|
+
}
|
|
51
|
+
def version = new groovy.json.JsonSlurper().parseText(candidate.text).version
|
|
52
|
+
def match = version =~ /^(\d+)\.(\d+)\./
|
|
53
|
+
if (match) {
|
|
54
|
+
return [match[0][1].toInteger(), match[0][2].toInteger()]
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return null
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// What the app asked for, or null when it did not ask. The React Native Gradle Plugin ORs the two
|
|
61
|
+
// spellings, so either one set to true turns the New Architecture on and a plain
|
|
62
|
+
// newArchEnabled=false does not veto a react-scoped true.
|
|
63
|
+
def newArchProperty() {
|
|
64
|
+
def names = ["newArchEnabled", "react.newArchEnabled"]
|
|
65
|
+
if (names.any { project.hasProperty(it) && project.property(it).toString().toBoolean() }) {
|
|
66
|
+
return true
|
|
67
|
+
}
|
|
68
|
+
if (names.any { project.hasProperty(it) }) {
|
|
69
|
+
return false
|
|
70
|
+
}
|
|
71
|
+
return null
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// The architecture is the app's choice; this answers it the way the React Native Gradle Plugin
|
|
75
|
+
// does. Below 0.82 the app opts in with newArchEnabled; from 0.82 React Native is bridgeless only
|
|
76
|
+
// and ignores the flag. Guessing wrong is silent and lands on the customer: src/oldarch compiled
|
|
77
|
+
// into a bridgeless app leaves TurboModuleRegistry.get() answering null and every plugin call
|
|
78
|
+
// throwing, with a green build. So an undecidable case fails the build instead.
|
|
79
|
+
def resolveNewArchitectureEnabled() {
|
|
80
|
+
def version = reactNativeVersion()
|
|
81
|
+
if (version != null && (version[0] > 0 || version[1] >= 82)) {
|
|
82
|
+
return true
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
def asked = newArchProperty()
|
|
86
|
+
if (asked != null) {
|
|
87
|
+
return asked
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (version == null) {
|
|
91
|
+
throw new GradleException(
|
|
92
|
+
"pushwoosh-react-native-plugin: cannot tell which React Native architecture to build. " +
|
|
93
|
+
"No react-native/package.json was found from ${rootProject.projectDir} and neither " +
|
|
94
|
+
"newArchEnabled nor react.newArchEnabled is set. Set newArchEnabled in " +
|
|
95
|
+
"android/gradle.properties to the architecture the app runs.")
|
|
96
|
+
}
|
|
97
|
+
return false
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Once per configuration: the lookup walks the file system and may spawn node.
|
|
101
|
+
if (!project.hasProperty("pushwooshNewArchEnabled")) {
|
|
102
|
+
project.ext.pushwooshNewArchEnabled = resolveNewArchitectureEnabled()
|
|
103
|
+
}
|
|
104
|
+
def isNewArchitectureEnabled = project.ext.pushwooshNewArchEnabled
|
|
105
|
+
|
|
23
106
|
apply plugin: 'com.android.library'
|
|
107
|
+
if (isNewArchitectureEnabled) {
|
|
108
|
+
// Generates NativePushwooshSpec from the spec in ../specs, configured by ../package.json
|
|
109
|
+
apply plugin: 'com.facebook.react'
|
|
110
|
+
}
|
|
24
111
|
|
|
25
112
|
android {
|
|
26
113
|
compileSdkVersion 34
|
|
27
114
|
defaultConfig {
|
|
28
115
|
minSdkVersion 21
|
|
29
116
|
targetSdkVersion 34
|
|
117
|
+
// What a consuming app must keep to run minified. The library itself is not minified, so
|
|
118
|
+
// proguardFiles here would configure nothing; consumerProguardFiles is what travels.
|
|
119
|
+
consumerProguardFiles 'proguard-rules.pro'
|
|
30
120
|
}
|
|
31
121
|
buildTypes {
|
|
32
122
|
release {
|
|
33
123
|
minifyEnabled false
|
|
34
|
-
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
sourceSets {
|
|
127
|
+
main {
|
|
128
|
+
if (isNewArchitectureEnabled) {
|
|
129
|
+
java.srcDirs += ["src/newarch/java"]
|
|
130
|
+
} else {
|
|
131
|
+
java.srcDirs += ["src/oldarch/java"]
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// src/test runs under both architectures; the per-architecture suites assert what the
|
|
135
|
+
// switch above must have produced
|
|
136
|
+
test {
|
|
137
|
+
if (isNewArchitectureEnabled) {
|
|
138
|
+
java.srcDirs += ["src/testNewarch/java"]
|
|
139
|
+
} else {
|
|
140
|
+
java.srcDirs += ["src/testOldarch/java"]
|
|
141
|
+
}
|
|
35
142
|
}
|
|
36
143
|
}
|
|
37
144
|
}
|
|
38
145
|
|
|
39
146
|
ext {
|
|
40
|
-
pushwoosh = "6.
|
|
147
|
+
pushwoosh = "6.13.0"
|
|
41
148
|
}
|
|
42
149
|
|
|
43
150
|
dependencies {
|
|
44
|
-
implementation
|
|
151
|
+
implementation "com.facebook.react:react-android"
|
|
45
152
|
implementation "com.pushwoosh:pushwoosh:${pushwoosh}"
|
|
46
153
|
implementation "com.pushwoosh:pushwoosh-firebase:${pushwoosh}"
|
|
47
154
|
implementation "com.pushwoosh:pushwoosh-huawei:${pushwoosh}"
|
|
@@ -54,5 +161,10 @@ dependencies {
|
|
|
54
161
|
implementation "androidx.activity:activity:1.8.0"
|
|
55
162
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22"
|
|
56
163
|
implementation "com.google.firebase:firebase-messaging:23.1.0"
|
|
164
|
+
|
|
165
|
+
// Test-only, run with `make android-test`.
|
|
166
|
+
testImplementation "junit:junit:4.13.2"
|
|
167
|
+
testImplementation "org.robolectric:robolectric:4.14.1"
|
|
168
|
+
testImplementation "org.mockito:mockito-core:5.14.2"
|
|
57
169
|
}
|
|
58
170
|
|
|
@@ -15,3 +15,19 @@
|
|
|
15
15
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
|
16
16
|
# public *;
|
|
17
17
|
#}
|
|
18
|
+
|
|
19
|
+
# Shipped to the consuming app through consumerProguardFiles.
|
|
20
|
+
#
|
|
21
|
+
# The Pushwoosh SDK stores its requests with WorkManager, which keeps them in a Room database.
|
|
22
|
+
# room-runtime ships `-keep class * extends androidx.room.RoomDatabase`, which holds the generated
|
|
23
|
+
# class but not the constructor Room instantiates it with, and the R8 full mode AGP 8 runs by
|
|
24
|
+
# default drops that constructor. A minified Release app then dies on startup in androidx.startup
|
|
25
|
+
# with "Failed to create an instance of class androidx.work.impl.WorkDatabase".
|
|
26
|
+
-keep class * extends androidx.room.RoomDatabase { <init>(); }
|
|
27
|
+
|
|
28
|
+
# The same shape one layer up: WorkManager instantiates the work request's input merger by name,
|
|
29
|
+
# and work-runtime ships `-keep class * extends androidx.work.InputMerger`, which holds the class
|
|
30
|
+
# but not that constructor. Under R8 full mode every worker the SDK enqueues then dies before it
|
|
31
|
+
# runs with "Could not create Input Merger androidx.work.OverwritingInputMerger", so registering
|
|
32
|
+
# for pushes reports neither a token nor an error - the app just never hears back.
|
|
33
|
+
-keep class * extends androidx.work.InputMerger { <init>(); }
|
|
@@ -6,14 +6,13 @@ import android.util.Log;
|
|
|
6
6
|
import java.util.ArrayList;
|
|
7
7
|
import java.util.Iterator;
|
|
8
8
|
|
|
9
|
+
import com.facebook.react.bridge.Arguments;
|
|
9
10
|
import com.facebook.react.bridge.ReadableArray;
|
|
10
11
|
import com.facebook.react.bridge.ReadableMap;
|
|
11
12
|
import com.facebook.react.bridge.ReadableMapKeySetIterator;
|
|
12
13
|
import com.facebook.react.bridge.ReadableType;
|
|
13
14
|
import com.facebook.react.bridge.WritableArray;
|
|
14
15
|
import com.facebook.react.bridge.WritableMap;
|
|
15
|
-
import com.facebook.react.bridge.WritableNativeArray;
|
|
16
|
-
import com.facebook.react.bridge.WritableNativeMap;
|
|
17
16
|
import com.pushwoosh.inbox.data.InboxMessage;
|
|
18
17
|
import com.pushwoosh.internal.utils.JsonUtils;
|
|
19
18
|
import com.pushwoosh.internal.utils.PWLog;
|
|
@@ -115,7 +114,7 @@ public final class ConversionUtil {
|
|
|
115
114
|
}
|
|
116
115
|
|
|
117
116
|
public static WritableMap toWritableMap(JSONObject jsonObject) {
|
|
118
|
-
|
|
117
|
+
WritableMap result = Arguments.createMap();
|
|
119
118
|
|
|
120
119
|
Iterator<String> keys = jsonObject.keys();
|
|
121
120
|
while (keys.hasNext()) {
|
|
@@ -152,7 +151,7 @@ public final class ConversionUtil {
|
|
|
152
151
|
}
|
|
153
152
|
|
|
154
153
|
public static WritableArray toWritableArray(JSONArray array) {
|
|
155
|
-
|
|
154
|
+
WritableArray result = Arguments.createArray();
|
|
156
155
|
|
|
157
156
|
for (int i = 0; i < array.length(); i++) {
|
|
158
157
|
try {
|
|
@@ -9,6 +9,7 @@ import com.facebook.react.bridge.Callback;
|
|
|
9
9
|
import com.facebook.react.bridge.ReactContext;
|
|
10
10
|
import com.facebook.react.bridge.WritableMap;
|
|
11
11
|
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
|
12
|
+
import com.pushwoosh.internal.utils.PWLog;
|
|
12
13
|
|
|
13
14
|
public class EventDispatcher {
|
|
14
15
|
|
|
@@ -37,7 +38,15 @@ public class EventDispatcher {
|
|
|
37
38
|
|
|
38
39
|
List<Callback> list = subscribers.get(event);
|
|
39
40
|
for (Callback subscriber : list) {
|
|
40
|
-
|
|
41
|
+
try {
|
|
42
|
+
subscriber.invoke(objects);
|
|
43
|
+
} catch (Exception e) {
|
|
44
|
+
// A callback bound to a runtime that is already gone throws, and React Native
|
|
45
|
+
// throws on a second invocation of a callback as well. The subscribers behind
|
|
46
|
+
// it, and the clear() below, have to happen anyway - otherwise the bad callback
|
|
47
|
+
// stays in the list and takes down every dispatch that follows.
|
|
48
|
+
PWLog.exception(e);
|
|
49
|
+
}
|
|
41
50
|
}
|
|
42
51
|
|
|
43
52
|
// A native module is supposed to invoke its callback only once!
|
|
@@ -1,33 +1,46 @@
|
|
|
1
1
|
package com.pushwoosh.reactnativeplugin;
|
|
2
2
|
|
|
3
|
+
import androidx.annotation.Nullable;
|
|
4
|
+
|
|
5
|
+
import com.facebook.react.BaseReactPackage;
|
|
3
6
|
import com.facebook.react.ReactPackage;
|
|
4
|
-
import com.facebook.react.bridge.JavaScriptModule;
|
|
5
7
|
import com.facebook.react.bridge.NativeModule;
|
|
6
8
|
import com.facebook.react.bridge.ReactApplicationContext;
|
|
9
|
+
import com.facebook.react.module.model.ReactModuleInfo;
|
|
10
|
+
import com.facebook.react.module.model.ReactModuleInfoProvider;
|
|
11
|
+
import com.facebook.react.turbomodule.core.interfaces.TurboModule;
|
|
7
12
|
|
|
8
|
-
import java.util.
|
|
9
|
-
import java.util.
|
|
10
|
-
import java.util.List;
|
|
13
|
+
import java.util.HashMap;
|
|
14
|
+
import java.util.Map;
|
|
11
15
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
return Collections.emptyList();
|
|
16
|
-
}
|
|
16
|
+
// BaseReactPackage already implements ReactPackage; it is repeated because the autolinking scan
|
|
17
|
+
// of the React Native 0.74 CLI only recognises "implements ReactPackage" or "extends TurboReactPackage"
|
|
18
|
+
public class PushwooshPackage extends BaseReactPackage implements ReactPackage {
|
|
17
19
|
|
|
20
|
+
@Nullable
|
|
18
21
|
@Override
|
|
19
|
-
public
|
|
20
|
-
|
|
22
|
+
public NativeModule getModule(String name, ReactApplicationContext reactContext) {
|
|
23
|
+
if (PushwooshPlugin.MODULE_NAME.equals(name)) {
|
|
24
|
+
return new PushwooshPlugin(reactContext);
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
21
27
|
}
|
|
22
28
|
|
|
23
29
|
@Override
|
|
24
|
-
public
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
public ReactModuleInfoProvider getReactModuleInfoProvider() {
|
|
31
|
+
return () -> {
|
|
32
|
+
// On the New Architecture PushwooshPluginSpec extends the codegen spec, which is a TurboModule
|
|
33
|
+
boolean isTurboModule = TurboModule.class.isAssignableFrom(PushwooshPlugin.class);
|
|
34
|
+
Map<String, ReactModuleInfo> modules = new HashMap<>();
|
|
35
|
+
modules.put(PushwooshPlugin.MODULE_NAME, new ReactModuleInfo(
|
|
36
|
+
PushwooshPlugin.MODULE_NAME,
|
|
37
|
+
PushwooshPlugin.class.getName(),
|
|
38
|
+
false, // canOverrideExistingModule
|
|
39
|
+
false, // needsEagerInit: the launch push is cached until JS calls init
|
|
40
|
+
false, // isCxxModule
|
|
41
|
+
isTurboModule
|
|
42
|
+
));
|
|
43
|
+
return modules;
|
|
44
|
+
};
|
|
31
45
|
}
|
|
32
46
|
}
|
|
33
|
-
|
|
@@ -8,14 +8,14 @@ import android.graphics.Color;
|
|
|
8
8
|
import androidx.annotation.NonNull;
|
|
9
9
|
import androidx.annotation.Nullable;
|
|
10
10
|
|
|
11
|
+
import com.facebook.react.bridge.Arguments;
|
|
11
12
|
import com.facebook.react.bridge.Callback;
|
|
12
13
|
import com.facebook.react.bridge.LifecycleEventListener;
|
|
13
14
|
import com.facebook.react.bridge.ReactApplicationContext;
|
|
14
|
-
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
|
15
15
|
import com.facebook.react.bridge.ReactMethod;
|
|
16
16
|
import com.facebook.react.bridge.ReadableArray;
|
|
17
17
|
import com.facebook.react.bridge.ReadableMap;
|
|
18
|
-
import com.facebook.react.bridge.
|
|
18
|
+
import com.facebook.react.bridge.WritableArray;
|
|
19
19
|
import com.pushwoosh.Pushwoosh;
|
|
20
20
|
import com.pushwoosh.RegisterForPushNotificationsResultData;
|
|
21
21
|
import com.pushwoosh.badge.PushwooshBadge;
|
|
@@ -50,26 +50,50 @@ import java.util.Collection;
|
|
|
50
50
|
import java.util.HashMap;
|
|
51
51
|
import java.util.Map;
|
|
52
52
|
|
|
53
|
-
public class PushwooshPlugin extends
|
|
53
|
+
public class PushwooshPlugin extends PushwooshPluginSpec implements LifecycleEventListener {
|
|
54
54
|
|
|
55
55
|
static final String TAG = "ReactNativePlugin";
|
|
56
|
+
// Same as the iOS class name: React Native resolves a TurboModule by class name first
|
|
57
|
+
public static final String MODULE_NAME = "PushwooshPlugin";
|
|
56
58
|
private static final String PUSH_OPEN_EVENT = "PwPushOpened";
|
|
57
59
|
private static final String PUSH_OPEN_JS_EVENT = "pushOpened";
|
|
58
60
|
|
|
59
61
|
private static final String PUSH_RECEIVED_EVENT = Pushwoosh.PUSH_RECEIVE_EVENT;
|
|
60
62
|
private static final String PUSH_RECEIVED_JS_EVENT = "pushReceived";
|
|
61
63
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
private
|
|
66
|
-
|
|
67
|
-
private
|
|
68
|
-
private
|
|
69
|
-
|
|
70
|
-
|
|
64
|
+
// Per bundle. The callbacks belong to the JS runtime that subscribed them and "JS has called
|
|
65
|
+
// init()" is true of that runtime alone, so this state goes away with the module React Native
|
|
66
|
+
// tears down - a late teardown has nothing left to reach into the next module with.
|
|
67
|
+
private final EventDispatcher mEventDispatcher = new EventDispatcher();
|
|
68
|
+
private boolean mInitialized = false;
|
|
69
|
+
private boolean mPushCallbackRegistered = false;
|
|
70
|
+
private boolean mReceivedPushCallbackRegistered = false;
|
|
71
|
+
|
|
72
|
+
// Per process. A push handed over before JS could take it outlives the React instance it
|
|
73
|
+
// arrived at: the next init() replays it.
|
|
74
|
+
private static PendingPush sReceivedPush;
|
|
75
|
+
private static PendingPush sStartPush;
|
|
71
76
|
private static final Object sStartPushLock = new Object();
|
|
72
77
|
|
|
78
|
+
// A push and what has already been done with it. Both paths to JS are replayed to a bundle
|
|
79
|
+
// that comes later - the device event by init(), the deprecated callback by onPushOpen() /
|
|
80
|
+
// onPushReceived() - and each of them only until the push has gone that way once. Without
|
|
81
|
+
// that, every new React instance was handed the last push of the process again: a pushOpened
|
|
82
|
+
// re-routing a deep link the app had already handled, and a launch push taking the place of
|
|
83
|
+
// the one-shot callback registration the new bundle was making.
|
|
84
|
+
private static final class PendingPush {
|
|
85
|
+
final String data;
|
|
86
|
+
boolean sentAsEvent;
|
|
87
|
+
boolean sentToCallback;
|
|
88
|
+
|
|
89
|
+
PendingPush(String data) {
|
|
90
|
+
this.data = data;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// The module JS is talking to, published on the first call from JS rather than in the
|
|
95
|
+
// constructor: TurboModuleManager.invalidate() constructs the module JS never required, and
|
|
96
|
+
// such a stillborn one must not take the live module's place.
|
|
73
97
|
private static PushwooshPlugin INSTANCE = null;
|
|
74
98
|
|
|
75
99
|
private InboxUiStyleManager inboxUiInboxUiStyleManager;
|
|
@@ -77,8 +101,6 @@ public class PushwooshPlugin extends ReactContextBaseJavaModule implements Lifec
|
|
|
77
101
|
public PushwooshPlugin(ReactApplicationContext reactContext) {
|
|
78
102
|
super(reactContext);
|
|
79
103
|
|
|
80
|
-
INSTANCE = this;
|
|
81
|
-
|
|
82
104
|
reactContext.addLifecycleEventListener(this);
|
|
83
105
|
|
|
84
106
|
Context applicationContext = AndroidPlatformModule.getApplicationContext();
|
|
@@ -91,7 +113,7 @@ public class PushwooshPlugin extends ReactContextBaseJavaModule implements Lifec
|
|
|
91
113
|
|
|
92
114
|
@Override
|
|
93
115
|
public String getName() {
|
|
94
|
-
return
|
|
116
|
+
return MODULE_NAME;
|
|
95
117
|
}
|
|
96
118
|
|
|
97
119
|
@ReactMethod
|
|
@@ -120,16 +142,22 @@ public class PushwooshPlugin extends ReactContextBaseJavaModule implements Lifec
|
|
|
120
142
|
Pushwoosh.getInstance().setAppId(appId);
|
|
121
143
|
|
|
122
144
|
synchronized (sStartPushLock) {
|
|
123
|
-
if (
|
|
124
|
-
sendEvent(PUSH_RECEIVED_JS_EVENT, ConversionUtil.stringToJSONObject(
|
|
145
|
+
if (sReceivedPush != null && !sReceivedPush.sentAsEvent) {
|
|
146
|
+
sendEvent(PUSH_RECEIVED_JS_EVENT, ConversionUtil.stringToJSONObject(sReceivedPush.data));
|
|
147
|
+
sReceivedPush.sentAsEvent = true;
|
|
125
148
|
}
|
|
126
149
|
|
|
127
|
-
if (
|
|
128
|
-
sendEvent(PUSH_OPEN_JS_EVENT, ConversionUtil.stringToJSONObject(
|
|
150
|
+
if (sStartPush != null && !sStartPush.sentAsEvent) {
|
|
151
|
+
sendEvent(PUSH_OPEN_JS_EVENT, ConversionUtil.stringToJSONObject(sStartPush.data));
|
|
152
|
+
sStartPush.sentAsEvent = true;
|
|
129
153
|
}
|
|
130
|
-
}
|
|
131
154
|
|
|
132
|
-
|
|
155
|
+
// Under the same lock as the replay above: a push taking the lock between the replay
|
|
156
|
+
// and this flag would find the gate closed, skip its own send, and never be replayed
|
|
157
|
+
// either.
|
|
158
|
+
mInitialized = true;
|
|
159
|
+
INSTANCE = this;
|
|
160
|
+
}
|
|
133
161
|
|
|
134
162
|
if (success != null) {
|
|
135
163
|
success.invoke();
|
|
@@ -137,7 +165,7 @@ public class PushwooshPlugin extends ReactContextBaseJavaModule implements Lifec
|
|
|
137
165
|
}
|
|
138
166
|
|
|
139
167
|
@ReactMethod
|
|
140
|
-
public void
|
|
168
|
+
public void registerForPushNotifications(final Callback success, final Callback error) {
|
|
141
169
|
Pushwoosh.getInstance().registerForPushNotifications(new RegisterForPushNotificationCallback(success, error));
|
|
142
170
|
}
|
|
143
171
|
|
|
@@ -162,13 +190,16 @@ public class PushwooshPlugin extends ReactContextBaseJavaModule implements Lifec
|
|
|
162
190
|
@ReactMethod
|
|
163
191
|
public void onPushOpen(Callback callback) {
|
|
164
192
|
synchronized (sStartPushLock) {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
193
|
+
INSTANCE = this;
|
|
194
|
+
|
|
195
|
+
if (!mPushCallbackRegistered && sStartPush != null && !sStartPush.sentToCallback) {
|
|
196
|
+
callback.invoke(ConversionUtil.toWritableMap(ConversionUtil.stringToJSONObject(sStartPush.data)));
|
|
197
|
+
sStartPush.sentToCallback = true;
|
|
198
|
+
mPushCallbackRegistered = true;
|
|
168
199
|
return;
|
|
169
200
|
}
|
|
170
201
|
|
|
171
|
-
|
|
202
|
+
mPushCallbackRegistered = true;
|
|
172
203
|
mEventDispatcher.subscribe(PUSH_OPEN_EVENT, callback);
|
|
173
204
|
}
|
|
174
205
|
}
|
|
@@ -176,13 +207,16 @@ public class PushwooshPlugin extends ReactContextBaseJavaModule implements Lifec
|
|
|
176
207
|
@ReactMethod
|
|
177
208
|
public void onPushReceived(Callback callback) {
|
|
178
209
|
synchronized (sStartPushLock) {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
210
|
+
INSTANCE = this;
|
|
211
|
+
|
|
212
|
+
if (!mReceivedPushCallbackRegistered && sReceivedPush != null && !sReceivedPush.sentToCallback) {
|
|
213
|
+
callback.invoke(ConversionUtil.toWritableMap(ConversionUtil.stringToJSONObject(sReceivedPush.data)));
|
|
214
|
+
sReceivedPush.sentToCallback = true;
|
|
215
|
+
mReceivedPushCallbackRegistered = true;
|
|
182
216
|
return;
|
|
183
217
|
}
|
|
184
218
|
|
|
185
|
-
|
|
219
|
+
mReceivedPushCallbackRegistered = true;
|
|
186
220
|
mEventDispatcher.subscribe(PUSH_RECEIVED_EVENT, callback);
|
|
187
221
|
}
|
|
188
222
|
}
|
|
@@ -193,7 +227,7 @@ public class PushwooshPlugin extends ReactContextBaseJavaModule implements Lifec
|
|
|
193
227
|
}
|
|
194
228
|
|
|
195
229
|
@ReactMethod
|
|
196
|
-
public void removeListeners(
|
|
230
|
+
public void removeListeners(double count) {
|
|
197
231
|
// Required for NativeEventEmitter. No need to do anything here.
|
|
198
232
|
}
|
|
199
233
|
|
|
@@ -313,22 +347,22 @@ public class PushwooshPlugin extends ReactContextBaseJavaModule implements Lifec
|
|
|
313
347
|
|
|
314
348
|
JSONObject params = ConversionUtil.toJsonObject(data);
|
|
315
349
|
|
|
350
|
+
// optString answers "" for a missing key, never null: without a message there is nothing
|
|
351
|
+
// to show, and empty user data has no business reaching the payload.
|
|
316
352
|
String message = params.optString("msg");
|
|
317
|
-
if (message
|
|
353
|
+
if (message.isEmpty()){
|
|
354
|
+
PWLog.error(TAG, "createLocalNotification: msg is required");
|
|
318
355
|
return;
|
|
319
356
|
}
|
|
320
357
|
int seconds = params.optInt("seconds");
|
|
321
358
|
Bundle extras = new Bundle();
|
|
322
359
|
String userData = params.optString("userData");
|
|
323
|
-
if (userData
|
|
360
|
+
if (!userData.isEmpty()){
|
|
324
361
|
extras.putString("u", userData);
|
|
325
362
|
}
|
|
326
363
|
|
|
327
|
-
LocalNotification
|
|
328
|
-
|
|
329
|
-
builder.setExtras(extras);
|
|
330
|
-
}
|
|
331
|
-
LocalNotification notification = builder
|
|
364
|
+
LocalNotification notification = new LocalNotification.Builder()
|
|
365
|
+
.setExtras(extras)
|
|
332
366
|
.setMessage(message)
|
|
333
367
|
.setDelay(seconds)
|
|
334
368
|
.build();
|
|
@@ -351,8 +385,8 @@ public class PushwooshPlugin extends ReactContextBaseJavaModule implements Lifec
|
|
|
351
385
|
}
|
|
352
386
|
|
|
353
387
|
@ReactMethod
|
|
354
|
-
public void setApplicationIconBadgeNumber(
|
|
355
|
-
PushwooshBadge.setBadgeNumber(badgeNumber);
|
|
388
|
+
public void setApplicationIconBadgeNumber(double badgeNumber) {
|
|
389
|
+
PushwooshBadge.setBadgeNumber((int) badgeNumber);
|
|
356
390
|
}
|
|
357
391
|
|
|
358
392
|
@ReactMethod
|
|
@@ -361,8 +395,8 @@ public class PushwooshPlugin extends ReactContextBaseJavaModule implements Lifec
|
|
|
361
395
|
}
|
|
362
396
|
|
|
363
397
|
@ReactMethod
|
|
364
|
-
public void addToApplicationIconBadgeNumber(
|
|
365
|
-
PushwooshBadge.addBadgeNumber(badgeNumber);
|
|
398
|
+
public void addToApplicationIconBadgeNumber(double badgeNumber) {
|
|
399
|
+
PushwooshBadge.addBadgeNumber((int) badgeNumber);
|
|
366
400
|
}
|
|
367
401
|
|
|
368
402
|
@ReactMethod
|
|
@@ -381,18 +415,22 @@ public class PushwooshPlugin extends ReactContextBaseJavaModule implements Lifec
|
|
|
381
415
|
}
|
|
382
416
|
|
|
383
417
|
@ReactMethod
|
|
384
|
-
public void setColorLED(
|
|
385
|
-
|
|
418
|
+
public void setColorLED(double color) {
|
|
419
|
+
// The spec types a colour as a JS number, so it arrives as a double, and an opaque ARGB
|
|
420
|
+
// value does not fit a signed int: 0xFFFF0000 is 4294901760. A direct double -> int
|
|
421
|
+
// narrowing clamps that to Integer.MAX_VALUE, while going through long keeps the bits the
|
|
422
|
+
// SDK expects (-65536).
|
|
423
|
+
PushwooshNotificationSettings.setColorLED((int) (long) color);
|
|
386
424
|
}
|
|
387
425
|
|
|
388
426
|
@ReactMethod
|
|
389
|
-
public void setSoundType(
|
|
390
|
-
PushwooshNotificationSettings.setSoundNotificationType(SoundType.fromInt(type));
|
|
427
|
+
public void setSoundType(double type) {
|
|
428
|
+
PushwooshNotificationSettings.setSoundNotificationType(SoundType.fromInt((int) type));
|
|
391
429
|
}
|
|
392
430
|
|
|
393
431
|
@ReactMethod
|
|
394
|
-
public void setVibrateType(
|
|
395
|
-
PushwooshNotificationSettings.setVibrateNotificationType(VibrateType.fromInt(type));
|
|
432
|
+
public void setVibrateType(double type) {
|
|
433
|
+
PushwooshNotificationSettings.setVibrateNotificationType(VibrateType.fromInt((int) type));
|
|
396
434
|
}
|
|
397
435
|
|
|
398
436
|
|
|
@@ -455,7 +493,7 @@ public class PushwooshPlugin extends ReactContextBaseJavaModule implements Lifec
|
|
|
455
493
|
try {
|
|
456
494
|
if (result.isSuccess() && result.getData() != null) {
|
|
457
495
|
ArrayList<InboxMessage> messagesList = new ArrayList<>(result.getData());
|
|
458
|
-
|
|
496
|
+
WritableArray writableArray = Arguments.createArray();
|
|
459
497
|
for (InboxMessage message : messagesList) {
|
|
460
498
|
writableArray.pushMap(ConversionUtil.inboxMessageToWritableMap(message));
|
|
461
499
|
}
|
|
@@ -551,8 +589,8 @@ public class PushwooshPlugin extends ReactContextBaseJavaModule implements Lifec
|
|
|
551
589
|
}
|
|
552
590
|
|
|
553
591
|
@ReactMethod
|
|
554
|
-
public void setRichMediaType(
|
|
555
|
-
RichMediaType richMediaType = type == 0 ? RichMediaType.MODAL : RichMediaType.DEFAULT;
|
|
592
|
+
public void setRichMediaType(double type) {
|
|
593
|
+
RichMediaType richMediaType = (int) type == 0 ? RichMediaType.MODAL : RichMediaType.DEFAULT;
|
|
556
594
|
RichMediaManager.setRichMediaType(richMediaType);
|
|
557
595
|
}
|
|
558
596
|
|
|
@@ -562,6 +600,17 @@ public class PushwooshPlugin extends ReactContextBaseJavaModule implements Lifec
|
|
|
562
600
|
callback.invoke(type.ordinal());
|
|
563
601
|
}
|
|
564
602
|
|
|
603
|
+
// iOS-only setting. Android shows a notification for a push that arrives in the foreground,
|
|
604
|
+
// which is what the iOS default reports; the methods exist so the spec is fully implemented.
|
|
605
|
+
@ReactMethod
|
|
606
|
+
public void setShowPushnotificationAlert(boolean showPushnotificationAlert) {
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
@ReactMethod
|
|
610
|
+
public void getShowPushnotificationAlert(Callback callback) {
|
|
611
|
+
callback.invoke(true);
|
|
612
|
+
}
|
|
613
|
+
|
|
565
614
|
///
|
|
566
615
|
/// LifecycleEventListener callbacks
|
|
567
616
|
///
|
|
@@ -580,11 +629,33 @@ public class PushwooshPlugin extends ReactContextBaseJavaModule implements Lifec
|
|
|
580
629
|
public void onHostDestroy() {
|
|
581
630
|
PWLog.noise(TAG, "Host destroyed");
|
|
582
631
|
|
|
583
|
-
|
|
584
|
-
|
|
632
|
+
// Nothing is reset here: the host going down is the activity, not the JS runtime. React
|
|
633
|
+
// Native keeps the context and the JS bundle across activities, JS does not call init() or
|
|
634
|
+
// onPushOpen() a second time, and a push opened after the user left by Back has to reach
|
|
635
|
+
// that bundle. Everything JS set up is reset in invalidate(), where the runtime ends.
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// React Native tears the module down with its React instance (legacy ModuleHolder.destroy(),
|
|
639
|
+
// TurboModuleManager.invalidate()). Everything JS set up lives on this module and goes with it;
|
|
640
|
+
// only the module's place as the live one has to be given up here. The cached push stays - the
|
|
641
|
+
// next init() replays it.
|
|
642
|
+
@Override
|
|
643
|
+
public void invalidate() {
|
|
644
|
+
super.invalidate();
|
|
645
|
+
PWLog.noise(TAG, "Module invalidated");
|
|
646
|
+
|
|
647
|
+
getReactApplicationContext().removeLifecycleEventListener(this);
|
|
585
648
|
|
|
586
|
-
|
|
587
|
-
|
|
649
|
+
// A push can be delivered on the FCM thread while the runtime is going down, and every
|
|
650
|
+
// other accessor of the field holds the lock.
|
|
651
|
+
synchronized (sStartPushLock) {
|
|
652
|
+
// Only if this module is still the live one. The next module publishes itself on the
|
|
653
|
+
// first call from JS and the order of the two events is not guaranteed, so a late
|
|
654
|
+
// teardown must not unseat a module that has already taken over.
|
|
655
|
+
if (INSTANCE == this) {
|
|
656
|
+
INSTANCE = null;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
588
659
|
}
|
|
589
660
|
|
|
590
661
|
///
|
|
@@ -596,12 +667,21 @@ public class PushwooshPlugin extends ReactContextBaseJavaModule implements Lifec
|
|
|
596
667
|
|
|
597
668
|
try {
|
|
598
669
|
synchronized (sStartPushLock) {
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
670
|
+
PendingPush push = new PendingPush(pushData);
|
|
671
|
+
sStartPush = push;
|
|
672
|
+
|
|
673
|
+
PushwooshPlugin plugin = INSTANCE;
|
|
674
|
+
if (plugin == null || !plugin.hasLiveRuntime()) {
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
if (plugin.mPushCallbackRegistered) {
|
|
679
|
+
plugin.mEventDispatcher.dispatchEvent(PUSH_OPEN_EVENT, ConversionUtil.toWritableMap(ConversionUtil.stringToJSONObject(pushData)));
|
|
680
|
+
push.sentToCallback = true;
|
|
602
681
|
}
|
|
603
|
-
if (
|
|
604
|
-
|
|
682
|
+
if (plugin.mInitialized) {
|
|
683
|
+
plugin.sendEvent(PUSH_OPEN_JS_EVENT, ConversionUtil.stringToJSONObject(pushData));
|
|
684
|
+
push.sentAsEvent = true;
|
|
605
685
|
}
|
|
606
686
|
}
|
|
607
687
|
} catch (Exception e) {
|
|
@@ -614,17 +694,33 @@ public class PushwooshPlugin extends ReactContextBaseJavaModule implements Lifec
|
|
|
614
694
|
mEventDispatcher.sendJSEvent(getReactApplicationContext(), event, ConversionUtil.toWritableMap(params));
|
|
615
695
|
}
|
|
616
696
|
|
|
697
|
+
// The React instance behind the module is still running. One on its way down is not: emitting
|
|
698
|
+
// into it throws, the catch around the callers swallows the failure, and the push is gone.
|
|
699
|
+
// Held back, it stays cached for the next init(). Callers hold sStartPushLock.
|
|
700
|
+
private boolean hasLiveRuntime() {
|
|
701
|
+
return getReactApplicationContext().hasActiveReactInstance();
|
|
702
|
+
}
|
|
703
|
+
|
|
617
704
|
static void messageReceived(String pushData) {
|
|
618
705
|
PWLog.info(TAG, "Push received: " + pushData);
|
|
619
706
|
|
|
620
707
|
try {
|
|
621
708
|
synchronized (sStartPushLock) {
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
709
|
+
PendingPush push = new PendingPush(pushData);
|
|
710
|
+
sReceivedPush = push;
|
|
711
|
+
|
|
712
|
+
PushwooshPlugin plugin = INSTANCE;
|
|
713
|
+
if (plugin == null || !plugin.hasLiveRuntime()) {
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
if (plugin.mReceivedPushCallbackRegistered) {
|
|
718
|
+
plugin.mEventDispatcher.dispatchEvent(PUSH_RECEIVED_EVENT, ConversionUtil.toWritableMap(ConversionUtil.stringToJSONObject(pushData)));
|
|
719
|
+
push.sentToCallback = true;
|
|
625
720
|
}
|
|
626
|
-
if (
|
|
627
|
-
|
|
721
|
+
if (plugin.mInitialized) {
|
|
722
|
+
plugin.sendEvent(PUSH_RECEIVED_JS_EVENT, ConversionUtil.stringToJSONObject(pushData));
|
|
723
|
+
push.sentAsEvent = true;
|
|
628
724
|
}
|
|
629
725
|
}
|
|
630
726
|
} catch (Exception e) {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
package com.pushwoosh.reactnativeplugin;
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
4
|
+
|
|
5
|
+
// New Architecture: NativePushwooshSpec is generated by codegen from src/specs/NativePushwoosh.ts.
|
|
6
|
+
abstract class PushwooshPluginSpec extends NativePushwooshSpec {
|
|
7
|
+
PushwooshPluginSpec(ReactApplicationContext reactContext) {
|
|
8
|
+
super(reactContext);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
package com.pushwoosh.reactnativeplugin;
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
4
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
|
5
|
+
|
|
6
|
+
// Legacy architecture: a plain bridge module whose methods are exported by @ReactMethod.
|
|
7
|
+
abstract class PushwooshPluginSpec extends ReactContextBaseJavaModule {
|
|
8
|
+
PushwooshPluginSpec(ReactApplicationContext reactContext) {
|
|
9
|
+
super(reactContext);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -24,6 +24,12 @@
|
|
|
24
24
|
#import "PushwooshInboxUI.h"
|
|
25
25
|
#endif
|
|
26
26
|
|
|
27
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
28
|
+
#import <RNPushwooshSpec/RNPushwooshSpec.h>
|
|
29
|
+
|
|
30
|
+
@interface PushwooshPlugin: RCTEventEmitter<NativePushwooshSpec, PushNotificationDelegate>
|
|
31
|
+
#else
|
|
27
32
|
@interface PushwooshPlugin: RCTEventEmitter<RCTBridgeModule, PushNotificationDelegate>
|
|
33
|
+
#endif
|
|
28
34
|
|
|
29
35
|
@end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//
|
|
2
|
-
// Pushwoosh.
|
|
2
|
+
// Pushwoosh.mm
|
|
3
3
|
// Pushwoosh React Native Plugin
|
|
4
4
|
// (c) Pushwoosh 2016
|
|
5
5
|
//
|
|
@@ -69,6 +69,7 @@ static PWEmailsCompletion emailsCompletionHandler(NSUInteger addressCount, RCTRe
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
static NSDictionary * gStartPushData = nil;
|
|
72
|
+
static BOOL gStartPushReplayed = NO; // Launch push already replayed into the JS events by init:
|
|
72
73
|
static NSURL * gPushDeepLinkURL = nil; // Deep link URL for New Architecture support
|
|
73
74
|
static NSString * gEarlyHandledHash = nil; // Last push hash the cold-start delegate accepted, to avoid double handling
|
|
74
75
|
static NSString * const kRegistrationSuccesEvent = @"PWRegistrationSuccess";
|
|
@@ -144,17 +145,17 @@ static void pwplugin_getInitialURL(id self, SEL _cmd, RCTPromiseResolveBlock res
|
|
|
144
145
|
}
|
|
145
146
|
}
|
|
146
147
|
|
|
147
|
-
void pushwoosh_swizzle(Class
|
|
148
|
+
void pushwoosh_swizzle(Class cls, SEL fromChange, SEL toChange, IMP impl, const char * signature) {
|
|
148
149
|
Method method = nil;
|
|
149
|
-
method = class_getInstanceMethod(
|
|
150
|
+
method = class_getInstanceMethod(cls, fromChange);
|
|
150
151
|
|
|
151
152
|
if (method) {
|
|
152
153
|
//method exists add a new method and swap with original
|
|
153
|
-
class_addMethod(
|
|
154
|
-
method_exchangeImplementations(class_getInstanceMethod(
|
|
154
|
+
class_addMethod(cls, toChange, impl, signature);
|
|
155
|
+
method_exchangeImplementations(class_getInstanceMethod(cls, fromChange), class_getInstanceMethod(cls, toChange));
|
|
155
156
|
} else {
|
|
156
157
|
//just add as orignal method
|
|
157
|
-
class_addMethod(
|
|
158
|
+
class_addMethod(cls, fromChange, impl, signature);
|
|
158
159
|
}
|
|
159
160
|
}
|
|
160
161
|
|
|
@@ -173,7 +174,15 @@ void pushwoosh_swizzle(Class class, SEL fromChange, SEL toChange, IMP impl, cons
|
|
|
173
174
|
|
|
174
175
|
#pragma mark - Pushwoosh RCTBridgeModule
|
|
175
176
|
|
|
176
|
-
|
|
177
|
+
// No custom name: React Native looks a TurboModule up by NSClassFromString(name) before it
|
|
178
|
+
// consults the RCT_EXPORT_MODULE registry, and "Pushwoosh" is the native SDK's own class.
|
|
179
|
+
RCT_EXPORT_MODULE();
|
|
180
|
+
|
|
181
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
182
|
+
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params {
|
|
183
|
+
return std::make_shared<facebook::react::NativePushwooshSpecJSI>(params);
|
|
184
|
+
}
|
|
185
|
+
#endif
|
|
177
186
|
|
|
178
187
|
- (dispatch_queue_t)methodQueue {
|
|
179
188
|
return dispatch_get_main_queue();
|
|
@@ -250,32 +259,43 @@ RCT_EXPORT_METHOD(init:(NSDictionary*)config success:(RCTResponseSenderBlock)suc
|
|
|
250
259
|
success(@[]);
|
|
251
260
|
}
|
|
252
261
|
|
|
253
|
-
|
|
254
|
-
|
|
262
|
+
// Once per launch push. Neither source clears itself here - gStartPushData is consumed by the
|
|
263
|
+
// legacy onPushOpen:, launchNotification belongs to the SDK - so a second init() (a consent
|
|
264
|
+
// flow initialising after the user agreed, an app switching application code, a Fast Refresh
|
|
265
|
+
// reload) used to replay the same push as pushReceived and pushOpened again and to re-arm a
|
|
266
|
+
// deep link that Linking.getInitialURL() had already consumed, sending the user back to the
|
|
267
|
+
// screen the push opened the first time.
|
|
268
|
+
if (!gStartPushReplayed) {
|
|
269
|
+
gStartPushReplayed = YES;
|
|
255
270
|
|
|
256
|
-
if (
|
|
257
|
-
|
|
258
|
-
gPushDeepLinkURL = deepLink;
|
|
259
|
-
}
|
|
271
|
+
if (gStartPushData) {
|
|
272
|
+
NSURL *deepLink = pwplugin_deepLinkURLFromPushLink(gStartPushData[@"l"]);
|
|
260
273
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
274
|
+
if (deepLink) {
|
|
275
|
+
if (!gPushDeepLinkURL) {
|
|
276
|
+
gPushDeepLinkURL = deepLink;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Writing the link into launchOptions is what makes RCTLinking report it, but only
|
|
280
|
+
// a real bridge accepts the write. Bridgeless reads it from the swizzled
|
|
281
|
+
// getInitialURL.
|
|
282
|
+
if (pwplugin_isRealBridge(self.bridge)) {
|
|
283
|
+
NSMutableDictionary *launchOptions = self.bridge.launchOptions.mutableCopy;
|
|
284
|
+
launchOptions[UIApplicationLaunchOptionsURLKey] = deepLink;
|
|
285
|
+
[self.bridge setValue:launchOptions forKey:@"launchOptions"];
|
|
286
|
+
}
|
|
267
287
|
}
|
|
288
|
+
|
|
289
|
+
[self sendJSEvent:kPushReceivedJSEvent withArgs:gStartPushData];
|
|
290
|
+
[self sendJSEvent:kPushOpenJSEvent withArgs:gStartPushData];
|
|
291
|
+
} else if([PushNotificationManager pushManager].launchNotification) {
|
|
292
|
+
[self sendJSEvent:kPushReceivedJSEvent withArgs:[PushNotificationManager pushManager].launchNotification];
|
|
293
|
+
[self sendJSEvent:kPushOpenJSEvent withArgs:[PushNotificationManager pushManager].launchNotification];
|
|
268
294
|
}
|
|
269
|
-
|
|
270
|
-
[self sendJSEvent:kPushReceivedJSEvent withArgs:gStartPushData];
|
|
271
|
-
[self sendJSEvent:kPushOpenJSEvent withArgs:gStartPushData];
|
|
272
|
-
} else if([PushNotificationManager pushManager].launchNotification) {
|
|
273
|
-
[self sendJSEvent:kPushReceivedJSEvent withArgs:[PushNotificationManager pushManager].launchNotification];
|
|
274
|
-
[self sendJSEvent:kPushOpenJSEvent withArgs:[PushNotificationManager pushManager].launchNotification];
|
|
275
295
|
}
|
|
276
296
|
}
|
|
277
297
|
|
|
278
|
-
RCT_EXPORT_METHOD(
|
|
298
|
+
RCT_EXPORT_METHOD(registerForPushNotifications:(RCTResponseSenderBlock)success error:(RCTResponseSenderBlock)error) {
|
|
279
299
|
[[PWEventDispatcher sharedDispatcher] subscribe:success toEvent:kRegistrationSuccesEvent];
|
|
280
300
|
[[PWEventDispatcher sharedDispatcher] subscribe:error toEvent:kRegistrationErrorEvent];
|
|
281
301
|
|
|
@@ -410,13 +430,13 @@ RCT_EXPORT_METHOD(setUserId:(NSString*)userId success:(RCTResponseSenderBlock)su
|
|
|
410
430
|
}];
|
|
411
431
|
}
|
|
412
432
|
|
|
413
|
-
RCT_EXPORT_METHOD(postEvent:(NSString*)event
|
|
433
|
+
RCT_EXPORT_METHOD(postEvent:(NSString*)event attributes:(NSDictionary*)attributes) {
|
|
414
434
|
[[PWInAppManager sharedManager] postEvent:event withAttributes:attributes];
|
|
415
435
|
}
|
|
416
436
|
|
|
417
|
-
RCT_EXPORT_METHOD(setApplicationIconBadgeNumber:(
|
|
437
|
+
RCT_EXPORT_METHOD(setApplicationIconBadgeNumber:(double)badgeNumber) {
|
|
418
438
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
419
|
-
[UIApplication sharedApplication].applicationIconBadgeNumber =
|
|
439
|
+
[UIApplication sharedApplication].applicationIconBadgeNumber = (NSInteger)badgeNumber;
|
|
420
440
|
});
|
|
421
441
|
}
|
|
422
442
|
|
|
@@ -432,9 +452,9 @@ RCT_EXPORT_METHOD(getApplicationIconBadgeNumber:(RCTResponseSenderBlock)callback
|
|
|
432
452
|
}
|
|
433
453
|
}
|
|
434
454
|
|
|
435
|
-
RCT_EXPORT_METHOD(addToApplicationIconBadgeNumber:(
|
|
455
|
+
RCT_EXPORT_METHOD(addToApplicationIconBadgeNumber:(double)badgeNumber) {
|
|
436
456
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
437
|
-
[UIApplication sharedApplication].applicationIconBadgeNumber +=
|
|
457
|
+
[UIApplication sharedApplication].applicationIconBadgeNumber += (NSInteger)badgeNumber;
|
|
438
458
|
});
|
|
439
459
|
}
|
|
440
460
|
|
|
@@ -448,7 +468,7 @@ RCT_EXPORT_METHOD(presentInboxUI:(NSDictionary *)styleDictionary) {
|
|
|
448
468
|
inboxViewController.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Close", @"Close") style:UIBarButtonItemStylePlain target:self action:@selector(closeInbox)];
|
|
449
469
|
[[PushwooshPlugin findRootViewController] presentViewController:[[UINavigationController alloc] initWithRootViewController:inboxViewController] animated:YES completion:nil];
|
|
450
470
|
|
|
451
|
-
__weak
|
|
471
|
+
__weak __typeof__(self) wself = self;
|
|
452
472
|
inboxViewController.onMessageClickBlock = ^(NSObject<PWInboxMessageProtocol> *message) {
|
|
453
473
|
if (message.type == PWInboxMessageTypeDeeplink) {
|
|
454
474
|
[wself closeInbox];
|
|
@@ -484,15 +504,19 @@ RCT_EXPORT_METHOD(messagesCount:(RCTResponseSenderBlock)callback) {
|
|
|
484
504
|
|
|
485
505
|
RCT_EXPORT_METHOD(loadMessages:(RCTResponseSenderBlock)success fail:(RCTResponseSenderBlock)fail) {
|
|
486
506
|
[PWInbox loadMessagesWithCompletion:^(NSArray<NSObject<PWInboxMessageProtocol> *> *messages, NSError *error) {
|
|
507
|
+
if (error) {
|
|
508
|
+
if (fail) {
|
|
509
|
+
fail(@[ objectOrNull([error localizedDescription]) ]);
|
|
510
|
+
}
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
|
|
487
514
|
if (success) {
|
|
488
|
-
NSMutableArray*
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
success( @[ array ]);
|
|
494
|
-
} else if (error != nil && fail != nil) {
|
|
495
|
-
fail(@[ error ]);
|
|
515
|
+
NSMutableArray *array = [[NSMutableArray alloc] init];
|
|
516
|
+
for (NSObject<PWInboxMessageProtocol> *message in messages) {
|
|
517
|
+
[array addObject:[self inboxMessageToDictionary:message]];
|
|
518
|
+
}
|
|
519
|
+
success(@[ array ]);
|
|
496
520
|
}
|
|
497
521
|
}];
|
|
498
522
|
}
|
|
@@ -584,25 +608,50 @@ API_AVAILABLE(ios(10.0)) {
|
|
|
584
608
|
NSString *currentHash = content.userInfo[@"p"];
|
|
585
609
|
BOOL isNeedToImplementInPlugin = [[PushwooshPlugin getPluginImplementationInfoPlistKey] boolValue];
|
|
586
610
|
|
|
611
|
+
// 0 when this notification is not ours to present: someone else's, a repeat of the same
|
|
612
|
+
// message, or a Pushwoosh push the app asked us not to show an alert for.
|
|
613
|
+
UNNotificationPresentationOptions options = 0;
|
|
614
|
+
|
|
587
615
|
if (isPushwooshMessage && isNeedToImplementInPlugin && ![lastPushwooshHash isEqualToString:currentHash]) {
|
|
588
616
|
lastPushwooshHash = currentHash;
|
|
589
617
|
|
|
590
618
|
if ([PushNotificationManager pushManager].showPushnotificationAlert) {
|
|
591
|
-
|
|
619
|
+
options = UNNotificationPresentationOptionSound | UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionAlert;
|
|
592
620
|
} else {
|
|
593
621
|
[[PushNotificationManager pushManager] handlePushReceived:content.userInfo];
|
|
594
622
|
}
|
|
595
623
|
|
|
596
624
|
[self sendJSEvent:kPushReceivedJSEvent withArgs:content.userInfo];
|
|
597
625
|
}
|
|
598
|
-
|
|
626
|
+
|
|
627
|
+
// UNUserNotificationCenter waits for exactly one answer: a second call is an error it logs,
|
|
628
|
+
// and no call at all leaves the notification unpresented. Both used to be reachable here -
|
|
629
|
+
// our own alert answered and the app's delegate answered again with the same handler, while a
|
|
630
|
+
// notification that is neither ours nor claimed by an app delegate was never answered at all.
|
|
631
|
+
// So whoever answers first decides and the rest is swallowed.
|
|
632
|
+
__block BOOL answered = NO;
|
|
633
|
+
void (^answerOnce)(UNNotificationPresentationOptions) = ^(UNNotificationPresentationOptions presentationOptions) {
|
|
634
|
+
if (answered) {
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
answered = YES;
|
|
638
|
+
completionHandler(presentationOptions);
|
|
639
|
+
};
|
|
640
|
+
|
|
641
|
+
// The SDK's alert goes up straight away, as before.
|
|
642
|
+
if (options != 0) {
|
|
643
|
+
answerOnce(options);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// The app's own delegate still gets the callback either way. A Pushwoosh message is handed
|
|
647
|
+
// over with a delay, so the SDK presents its own alert first.
|
|
599
648
|
if (_originalNotificationCenterDelegate != nil &&
|
|
600
649
|
_originalNotificationCenterDelegateResponds.willPresentNotification) {
|
|
601
650
|
|
|
602
651
|
dispatch_block_t presentationBlock = ^{
|
|
603
652
|
[self->_originalNotificationCenterDelegate userNotificationCenter:center
|
|
604
653
|
willPresentNotification:notification
|
|
605
|
-
withCompletionHandler:
|
|
654
|
+
withCompletionHandler:answerOnce];
|
|
606
655
|
};
|
|
607
656
|
|
|
608
657
|
if (isPushwooshMessage) {
|
|
@@ -610,14 +659,10 @@ API_AVAILABLE(ios(10.0)) {
|
|
|
610
659
|
} else {
|
|
611
660
|
presentationBlock();
|
|
612
661
|
}
|
|
662
|
+
return;
|
|
613
663
|
}
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
_originalNotificationCenterDelegateResponds.willPresentNotification) {
|
|
617
|
-
[_originalNotificationCenterDelegate userNotificationCenter:center
|
|
618
|
-
willPresentNotification:notification
|
|
619
|
-
withCompletionHandler:completionHandler];
|
|
620
|
-
}
|
|
664
|
+
|
|
665
|
+
answerOnce(options);
|
|
621
666
|
}
|
|
622
667
|
|
|
623
668
|
- (BOOL)isContentAvailablePush:(NSDictionary *)userInfo {
|
|
@@ -737,7 +782,8 @@ API_AVAILABLE(ios(10.0)) {
|
|
|
737
782
|
|
|
738
783
|
- (NSString*)dateToString:(NSDate*)date {
|
|
739
784
|
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
|
|
740
|
-
[formatter
|
|
785
|
+
[formatter setLocale:[NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]];
|
|
786
|
+
[formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZ"];
|
|
741
787
|
return [formatter stringFromDate:date];
|
|
742
788
|
}
|
|
743
789
|
|
|
@@ -979,6 +1025,25 @@ RCT_EXPORT_METHOD(enableHuaweiPushNotifications) {
|
|
|
979
1025
|
// available in Android only
|
|
980
1026
|
}
|
|
981
1027
|
|
|
1028
|
+
#pragma mark - Android-only settings
|
|
1029
|
+
|
|
1030
|
+
// Declared so the TurboModule spec is fully implemented on iOS: a call from JS is a no-op here
|
|
1031
|
+
// instead of a TypeError. The settings exist only in the Android SDK.
|
|
1032
|
+
|
|
1033
|
+
RCT_EXPORT_METHOD(setMultiNotificationMode:(BOOL)on) {}
|
|
1034
|
+
|
|
1035
|
+
RCT_EXPORT_METHOD(setLightScreenOnNotification:(BOOL)on) {}
|
|
1036
|
+
|
|
1037
|
+
RCT_EXPORT_METHOD(setEnableLED:(BOOL)on) {}
|
|
1038
|
+
|
|
1039
|
+
RCT_EXPORT_METHOD(setColorLED:(double)color) {}
|
|
1040
|
+
|
|
1041
|
+
RCT_EXPORT_METHOD(setSoundType:(double)type) {}
|
|
1042
|
+
|
|
1043
|
+
RCT_EXPORT_METHOD(setVibrateType:(double)type) {}
|
|
1044
|
+
|
|
1045
|
+
RCT_EXPORT_METHOD(setNotificationIconBackgroundColor:(NSString *)color) {}
|
|
1046
|
+
|
|
982
1047
|
RCT_EXPORT_METHOD(registerSMSNumber:(NSString *)phoneNumber) {
|
|
983
1048
|
[[Pushwoosh sharedInstance] registerSmsNumber:phoneNumber];
|
|
984
1049
|
}
|
|
@@ -987,8 +1052,8 @@ RCT_EXPORT_METHOD(registerWhatsappNumber:(NSString *)phoneNumber) {
|
|
|
987
1052
|
[[Pushwoosh sharedInstance] registerWhatsappNumber:phoneNumber];
|
|
988
1053
|
}
|
|
989
1054
|
|
|
990
|
-
RCT_EXPORT_METHOD(setRichMediaType:(
|
|
991
|
-
PWRichMediaPresentationStyle style =
|
|
1055
|
+
RCT_EXPORT_METHOD(setRichMediaType:(double)type) {
|
|
1056
|
+
PWRichMediaPresentationStyle style = (NSInteger)type == 0
|
|
992
1057
|
? PWRichMediaPresentationStyleModal
|
|
993
1058
|
: PWRichMediaPresentationStyleLegacy;
|
|
994
1059
|
[Pushwoosh.media setRichMediaPresentationStyle:style];
|
|
@@ -1052,6 +1117,7 @@ RCT_EXPORT_METHOD(getRichMediaType:(RCTResponseSenderBlock)callback) {
|
|
|
1052
1117
|
- (void)onPushReceived:(PushNotificationManager *)manager withNotification:(NSDictionary *)pushNotification onStart:(BOOL)onStart {
|
|
1053
1118
|
if (onStart) {
|
|
1054
1119
|
gStartPushData = pushNotification;
|
|
1120
|
+
gStartPushReplayed = NO;
|
|
1055
1121
|
// Save deep link URL for New Architecture (Linking.getInitialURL support)
|
|
1056
1122
|
NSURL *deepLink = pwplugin_deepLinkURLFromPushLink(pushNotification[@"l"]);
|
|
1057
1123
|
if (deepLink) {
|
|
@@ -1062,6 +1128,7 @@ RCT_EXPORT_METHOD(getRichMediaType:(RCTResponseSenderBlock)callback) {
|
|
|
1062
1128
|
- (void)onPushAccepted:(PushNotificationManager *)manager withNotification:(NSDictionary *)pushNotification onStart:(BOOL)onStart {
|
|
1063
1129
|
if (onStart) {
|
|
1064
1130
|
gStartPushData = pushNotification;
|
|
1131
|
+
gStartPushReplayed = NO;
|
|
1065
1132
|
// Save deep link URL for New Architecture (Linking.getInitialURL support)
|
|
1066
1133
|
NSURL *deepLink = pwplugin_deepLinkURLFromPushLink(pushNotification[@"l"]);
|
|
1067
1134
|
if (deepLink) {
|
package/src/package.json
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type {TurboModule} from 'react-native';
|
|
2
|
+
import {TurboModuleRegistry} from 'react-native';
|
|
3
|
+
|
|
4
|
+
// Codegen input. Parameter names become the Objective-C selector labels (init:success:error:)
|
|
5
|
+
// and the Java parameter names, so they must match the native implementations.
|
|
6
|
+
export interface Spec extends TurboModule {
|
|
7
|
+
setReverseProxy(url: string, headers: Object | null): void;
|
|
8
|
+
init(config: Object, success: (result: Object) => void, error: (message: string) => void): void;
|
|
9
|
+
// `register` is a C++ keyword, so codegen cannot emit a method of that name
|
|
10
|
+
registerForPushNotifications(success: (token: string) => void, error: (message: string) => void): void;
|
|
11
|
+
unregister(success: (token: string) => void, error: (message: string) => void): void;
|
|
12
|
+
onPushOpen(callback: (push: Object) => void): void;
|
|
13
|
+
addListener(eventName: string): void;
|
|
14
|
+
removeListeners(count: number): void;
|
|
15
|
+
setEmails(emails: Array<string>, success: () => void, error: (message: string) => void): void;
|
|
16
|
+
setUserEmails(
|
|
17
|
+
userId: string,
|
|
18
|
+
emails: Array<string>,
|
|
19
|
+
success: () => void,
|
|
20
|
+
error: (message: string) => void,
|
|
21
|
+
): void;
|
|
22
|
+
registerSMSNumber(phoneNumber: string): void;
|
|
23
|
+
registerWhatsappNumber(phoneNumber: string): void;
|
|
24
|
+
setTags(tags: Object, success: () => void, error: (message: string) => void): void;
|
|
25
|
+
getTags(success: (tags: Object) => void, error: (message: string) => void): void;
|
|
26
|
+
setShowPushnotificationAlert(showPushnotificationAlert: boolean): void;
|
|
27
|
+
getShowPushnotificationAlert(callback: (showPushnotificationAlert: boolean) => void): void;
|
|
28
|
+
getPushToken(callback: (token: string) => void): void;
|
|
29
|
+
getHwid(callback: (hwid: string) => void): void;
|
|
30
|
+
getUserId(callback: (userId: string) => void): void;
|
|
31
|
+
setUserId(userId: string, success: () => void, error: (message: string) => void): void;
|
|
32
|
+
postEvent(event: string, attributes: Object): void;
|
|
33
|
+
setApplicationIconBadgeNumber(badgeNumber: number): void;
|
|
34
|
+
getApplicationIconBadgeNumber(callback: (badgeNumber: number) => void): void;
|
|
35
|
+
addToApplicationIconBadgeNumber(badgeNumber: number): void;
|
|
36
|
+
setMultiNotificationMode(on: boolean): void;
|
|
37
|
+
setLightScreenOnNotification(on: boolean): void;
|
|
38
|
+
setEnableLED(on: boolean): void;
|
|
39
|
+
setColorLED(color: number): void;
|
|
40
|
+
setSoundType(type: number): void;
|
|
41
|
+
setVibrateType(type: number): void;
|
|
42
|
+
presentInboxUI(style: Object | null): void;
|
|
43
|
+
messagesWithNoActionPerformedCount(callback: (count: number) => void): void;
|
|
44
|
+
unreadMessagesCount(callback: (count: number) => void): void;
|
|
45
|
+
messagesCount(callback: (count: number) => void): void;
|
|
46
|
+
loadMessages(success: (messages: Array<Object>) => void, fail: (message: string) => void): void;
|
|
47
|
+
readMessage(code: string): void;
|
|
48
|
+
readMessages(codes: Array<string>): void;
|
|
49
|
+
deleteMessage(code: string): void;
|
|
50
|
+
deleteMessages(codes: Array<string>): void;
|
|
51
|
+
performAction(code: string): void;
|
|
52
|
+
isCommunicationEnabled(callback: (enabled: boolean) => void): void;
|
|
53
|
+
setCommunicationEnabled(
|
|
54
|
+
enabled: boolean,
|
|
55
|
+
success: () => void,
|
|
56
|
+
error: (message: string) => void,
|
|
57
|
+
): void;
|
|
58
|
+
setNotificationIconBackgroundColor(color: string): void;
|
|
59
|
+
setLanguage(language: string): void;
|
|
60
|
+
enableHuaweiPushNotifications(): void;
|
|
61
|
+
setRichMediaType(type: number): void;
|
|
62
|
+
getRichMediaType(callback: (type: number) => void): void;
|
|
63
|
+
createLocalNotification(params: Object): void;
|
|
64
|
+
clearLocalNotification(): void;
|
|
65
|
+
clearNotificationCenter(): void;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// `get`, not `getEnforcing`: importing the package on a build without the native module (Jest,
|
|
69
|
+
// an unlinked app) must not throw; a method call does, as it always did.
|
|
70
|
+
//
|
|
71
|
+
// The module is named after its Objective-C class. React Native resolves a TurboModule by
|
|
72
|
+
// NSClassFromString(name) first, and "Pushwoosh" is the native SDK's own class.
|
|
73
|
+
export default TurboModuleRegistry.get<Spec>('PushwooshPlugin');
|