pushwoosh-react-native-plugin 6.1.60 → 7.0.0
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 +118 -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/PushwooshPackage.java +32 -19
- package/src/android/src/main/java/com/pushwoosh/reactnativeplugin/PushwooshPlugin.java +71 -32
- 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} +186 -56
- 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.0",
|
|
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.0"
|
|
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
|
-
|
|
18
|
-
|
|
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
|
+
|
|
22
|
+
s.dependency 'PushwooshXCFramework', '7.2.6'
|
|
19
23
|
s.dependency 'PushwooshInboxUIXCFramework'
|
|
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}"
|
|
@@ -49,7 +156,15 @@ dependencies {
|
|
|
49
156
|
implementation "com.pushwoosh:pushwoosh-badge:${pushwoosh}"
|
|
50
157
|
implementation "com.pushwoosh:pushwoosh-inbox:${pushwoosh}"
|
|
51
158
|
implementation "com.pushwoosh:pushwoosh-inbox-ui:${pushwoosh}"
|
|
159
|
+
// InboxActivity calls androidx.activity.EdgeToEdge, added in 1.8.0; inbox-ui itself
|
|
160
|
+
// resolves 1.6.0, so the inbox screen dies with ClassNotFoundException without this
|
|
161
|
+
implementation "androidx.activity:activity:1.8.0"
|
|
52
162
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22"
|
|
53
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"
|
|
54
169
|
}
|
|
55
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 {
|
|
@@ -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,9 +50,11 @@ 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
|
|
|
@@ -91,7 +93,7 @@ public class PushwooshPlugin extends ReactContextBaseJavaModule implements Lifec
|
|
|
91
93
|
|
|
92
94
|
@Override
|
|
93
95
|
public String getName() {
|
|
94
|
-
return
|
|
96
|
+
return MODULE_NAME;
|
|
95
97
|
}
|
|
96
98
|
|
|
97
99
|
@ReactMethod
|
|
@@ -127,9 +129,12 @@ public class PushwooshPlugin extends ReactContextBaseJavaModule implements Lifec
|
|
|
127
129
|
if (sStartPushData != null) {
|
|
128
130
|
sendEvent(PUSH_OPEN_JS_EVENT, ConversionUtil.stringToJSONObject(sStartPushData));
|
|
129
131
|
}
|
|
130
|
-
}
|
|
131
132
|
|
|
132
|
-
|
|
133
|
+
// Under the same lock as the replay above: a push taking the lock between the replay
|
|
134
|
+
// and this flag would find sInitialized false, skip its own send, and never be
|
|
135
|
+
// replayed either.
|
|
136
|
+
sInitialized = true;
|
|
137
|
+
}
|
|
133
138
|
|
|
134
139
|
if (success != null) {
|
|
135
140
|
success.invoke();
|
|
@@ -137,7 +142,7 @@ public class PushwooshPlugin extends ReactContextBaseJavaModule implements Lifec
|
|
|
137
142
|
}
|
|
138
143
|
|
|
139
144
|
@ReactMethod
|
|
140
|
-
public void
|
|
145
|
+
public void registerForPushNotifications(final Callback success, final Callback error) {
|
|
141
146
|
Pushwoosh.getInstance().registerForPushNotifications(new RegisterForPushNotificationCallback(success, error));
|
|
142
147
|
}
|
|
143
148
|
|
|
@@ -193,7 +198,7 @@ public class PushwooshPlugin extends ReactContextBaseJavaModule implements Lifec
|
|
|
193
198
|
}
|
|
194
199
|
|
|
195
200
|
@ReactMethod
|
|
196
|
-
public void removeListeners(
|
|
201
|
+
public void removeListeners(double count) {
|
|
197
202
|
// Required for NativeEventEmitter. No need to do anything here.
|
|
198
203
|
}
|
|
199
204
|
|
|
@@ -313,22 +318,22 @@ public class PushwooshPlugin extends ReactContextBaseJavaModule implements Lifec
|
|
|
313
318
|
|
|
314
319
|
JSONObject params = ConversionUtil.toJsonObject(data);
|
|
315
320
|
|
|
321
|
+
// optString answers "" for a missing key, never null: without a message there is nothing
|
|
322
|
+
// to show, and empty user data has no business reaching the payload.
|
|
316
323
|
String message = params.optString("msg");
|
|
317
|
-
if (message
|
|
324
|
+
if (message.isEmpty()){
|
|
325
|
+
PWLog.error(TAG, "createLocalNotification: msg is required");
|
|
318
326
|
return;
|
|
319
327
|
}
|
|
320
328
|
int seconds = params.optInt("seconds");
|
|
321
329
|
Bundle extras = new Bundle();
|
|
322
330
|
String userData = params.optString("userData");
|
|
323
|
-
if (userData
|
|
331
|
+
if (!userData.isEmpty()){
|
|
324
332
|
extras.putString("u", userData);
|
|
325
333
|
}
|
|
326
334
|
|
|
327
|
-
LocalNotification
|
|
328
|
-
|
|
329
|
-
builder.setExtras(extras);
|
|
330
|
-
}
|
|
331
|
-
LocalNotification notification = builder
|
|
335
|
+
LocalNotification notification = new LocalNotification.Builder()
|
|
336
|
+
.setExtras(extras)
|
|
332
337
|
.setMessage(message)
|
|
333
338
|
.setDelay(seconds)
|
|
334
339
|
.build();
|
|
@@ -351,8 +356,8 @@ public class PushwooshPlugin extends ReactContextBaseJavaModule implements Lifec
|
|
|
351
356
|
}
|
|
352
357
|
|
|
353
358
|
@ReactMethod
|
|
354
|
-
public void setApplicationIconBadgeNumber(
|
|
355
|
-
PushwooshBadge.setBadgeNumber(badgeNumber);
|
|
359
|
+
public void setApplicationIconBadgeNumber(double badgeNumber) {
|
|
360
|
+
PushwooshBadge.setBadgeNumber((int) badgeNumber);
|
|
356
361
|
}
|
|
357
362
|
|
|
358
363
|
@ReactMethod
|
|
@@ -361,8 +366,8 @@ public class PushwooshPlugin extends ReactContextBaseJavaModule implements Lifec
|
|
|
361
366
|
}
|
|
362
367
|
|
|
363
368
|
@ReactMethod
|
|
364
|
-
public void addToApplicationIconBadgeNumber(
|
|
365
|
-
PushwooshBadge.addBadgeNumber(badgeNumber);
|
|
369
|
+
public void addToApplicationIconBadgeNumber(double badgeNumber) {
|
|
370
|
+
PushwooshBadge.addBadgeNumber((int) badgeNumber);
|
|
366
371
|
}
|
|
367
372
|
|
|
368
373
|
@ReactMethod
|
|
@@ -381,18 +386,22 @@ public class PushwooshPlugin extends ReactContextBaseJavaModule implements Lifec
|
|
|
381
386
|
}
|
|
382
387
|
|
|
383
388
|
@ReactMethod
|
|
384
|
-
public void setColorLED(
|
|
385
|
-
|
|
389
|
+
public void setColorLED(double color) {
|
|
390
|
+
// The spec types a colour as a JS number, so it arrives as a double, and an opaque ARGB
|
|
391
|
+
// value does not fit a signed int: 0xFFFF0000 is 4294901760. A direct double -> int
|
|
392
|
+
// narrowing clamps that to Integer.MAX_VALUE, while going through long keeps the bits the
|
|
393
|
+
// SDK expects (-65536).
|
|
394
|
+
PushwooshNotificationSettings.setColorLED((int) (long) color);
|
|
386
395
|
}
|
|
387
396
|
|
|
388
397
|
@ReactMethod
|
|
389
|
-
public void setSoundType(
|
|
390
|
-
PushwooshNotificationSettings.setSoundNotificationType(SoundType.fromInt(type));
|
|
398
|
+
public void setSoundType(double type) {
|
|
399
|
+
PushwooshNotificationSettings.setSoundNotificationType(SoundType.fromInt((int) type));
|
|
391
400
|
}
|
|
392
401
|
|
|
393
402
|
@ReactMethod
|
|
394
|
-
public void setVibrateType(
|
|
395
|
-
PushwooshNotificationSettings.setVibrateNotificationType(VibrateType.fromInt(type));
|
|
403
|
+
public void setVibrateType(double type) {
|
|
404
|
+
PushwooshNotificationSettings.setVibrateNotificationType(VibrateType.fromInt((int) type));
|
|
396
405
|
}
|
|
397
406
|
|
|
398
407
|
|
|
@@ -455,7 +464,7 @@ public class PushwooshPlugin extends ReactContextBaseJavaModule implements Lifec
|
|
|
455
464
|
try {
|
|
456
465
|
if (result.isSuccess() && result.getData() != null) {
|
|
457
466
|
ArrayList<InboxMessage> messagesList = new ArrayList<>(result.getData());
|
|
458
|
-
|
|
467
|
+
WritableArray writableArray = Arguments.createArray();
|
|
459
468
|
for (InboxMessage message : messagesList) {
|
|
460
469
|
writableArray.pushMap(ConversionUtil.inboxMessageToWritableMap(message));
|
|
461
470
|
}
|
|
@@ -551,8 +560,8 @@ public class PushwooshPlugin extends ReactContextBaseJavaModule implements Lifec
|
|
|
551
560
|
}
|
|
552
561
|
|
|
553
562
|
@ReactMethod
|
|
554
|
-
public void setRichMediaType(
|
|
555
|
-
RichMediaType richMediaType = type == 0 ? RichMediaType.MODAL : RichMediaType.DEFAULT;
|
|
563
|
+
public void setRichMediaType(double type) {
|
|
564
|
+
RichMediaType richMediaType = (int) type == 0 ? RichMediaType.MODAL : RichMediaType.DEFAULT;
|
|
556
565
|
RichMediaManager.setRichMediaType(richMediaType);
|
|
557
566
|
}
|
|
558
567
|
|
|
@@ -562,6 +571,17 @@ public class PushwooshPlugin extends ReactContextBaseJavaModule implements Lifec
|
|
|
562
571
|
callback.invoke(type.ordinal());
|
|
563
572
|
}
|
|
564
573
|
|
|
574
|
+
// iOS-only setting. Android shows a notification for a push that arrives in the foreground,
|
|
575
|
+
// which is what the iOS default reports; the methods exist so the spec is fully implemented.
|
|
576
|
+
@ReactMethod
|
|
577
|
+
public void setShowPushnotificationAlert(boolean showPushnotificationAlert) {
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
@ReactMethod
|
|
581
|
+
public void getShowPushnotificationAlert(Callback callback) {
|
|
582
|
+
callback.invoke(true);
|
|
583
|
+
}
|
|
584
|
+
|
|
565
585
|
///
|
|
566
586
|
/// LifecycleEventListener callbacks
|
|
567
587
|
///
|
|
@@ -580,11 +600,30 @@ public class PushwooshPlugin extends ReactContextBaseJavaModule implements Lifec
|
|
|
580
600
|
public void onHostDestroy() {
|
|
581
601
|
PWLog.noise(TAG, "Host destroyed");
|
|
582
602
|
|
|
583
|
-
|
|
584
|
-
|
|
603
|
+
// A push can be delivered on the FCM thread while the host is going down, and every other
|
|
604
|
+
// accessor of these fields holds the lock.
|
|
605
|
+
synchronized (sStartPushLock) {
|
|
606
|
+
// Only for the module this host is taking down. A restarted host builds a new
|
|
607
|
+
// PushwooshPlugin, and the order of the two events is not guaranteed, so a late
|
|
608
|
+
// teardown must not reset what the new module has already set up.
|
|
609
|
+
if (INSTANCE != this) {
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
585
612
|
|
|
586
|
-
|
|
587
|
-
|
|
613
|
+
sPushCallbackRegistered = false;
|
|
614
|
+
sStartPushData = null;
|
|
615
|
+
|
|
616
|
+
sReceivedPushCallbackRegistered = false;
|
|
617
|
+
sReceivedPushData = null;
|
|
618
|
+
|
|
619
|
+
// These are what openPush()/messageReceived() consult before sending to JS. Left
|
|
620
|
+
// standing, a push arriving after the host died was handed to a context with no JS
|
|
621
|
+
// runtime, the broad catch there swallowed the failure, and the push was gone for
|
|
622
|
+
// good - the cache that would have replayed it on the next init() had just been
|
|
623
|
+
// cleared above. Cleared, the same push is cached and the next init() replays it.
|
|
624
|
+
sInitialized = false;
|
|
625
|
+
INSTANCE = null;
|
|
626
|
+
}
|
|
588
627
|
}
|
|
589
628
|
|
|
590
629
|
///
|
|
@@ -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,7 +69,9 @@ 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
|
|
74
|
+
static NSString * gEarlyHandledHash = nil; // Last push hash the cold-start delegate accepted, to avoid double handling
|
|
73
75
|
static NSString * const kRegistrationSuccesEvent = @"PWRegistrationSuccess";
|
|
74
76
|
static NSString * const kRegistrationErrorEvent = @"PWRegistrationError";
|
|
75
77
|
static NSString * const kPushReceivedEvent = @"PWPushReceived";
|
|
@@ -99,6 +101,17 @@ static NSURL * pwplugin_deepLinkURLFromPushLink(id link) {
|
|
|
99
101
|
return url;
|
|
100
102
|
}
|
|
101
103
|
|
|
104
|
+
// Bridgeless hands a module an RCTBridgeProxy: an NSProxy that carries none of NSObject's KVC and
|
|
105
|
+
// turns an unknown selector into a logged no-op. object_getClass asks without messaging it.
|
|
106
|
+
static BOOL pwplugin_isRealBridge(id bridge) {
|
|
107
|
+
if (bridge == nil) {
|
|
108
|
+
return NO;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
Class bridgeProxyClass = NSClassFromString(@"RCTBridgeProxy");
|
|
112
|
+
return bridgeProxyClass == nil || object_getClass(bridge) != bridgeProxyClass;
|
|
113
|
+
}
|
|
114
|
+
|
|
102
115
|
@interface PushwooshPlugin (InnerPushwooshPlugin)
|
|
103
116
|
|
|
104
117
|
- (void) application:(UIApplication *)application pwplugin_didRegisterWithDeviceToken:(NSData *)deviceToken;
|
|
@@ -132,17 +145,17 @@ static void pwplugin_getInitialURL(id self, SEL _cmd, RCTPromiseResolveBlock res
|
|
|
132
145
|
}
|
|
133
146
|
}
|
|
134
147
|
|
|
135
|
-
void pushwoosh_swizzle(Class
|
|
148
|
+
void pushwoosh_swizzle(Class cls, SEL fromChange, SEL toChange, IMP impl, const char * signature) {
|
|
136
149
|
Method method = nil;
|
|
137
|
-
method = class_getInstanceMethod(
|
|
150
|
+
method = class_getInstanceMethod(cls, fromChange);
|
|
138
151
|
|
|
139
152
|
if (method) {
|
|
140
153
|
//method exists add a new method and swap with original
|
|
141
|
-
class_addMethod(
|
|
142
|
-
method_exchangeImplementations(class_getInstanceMethod(
|
|
154
|
+
class_addMethod(cls, toChange, impl, signature);
|
|
155
|
+
method_exchangeImplementations(class_getInstanceMethod(cls, fromChange), class_getInstanceMethod(cls, toChange));
|
|
143
156
|
} else {
|
|
144
157
|
//just add as orignal method
|
|
145
|
-
class_addMethod(
|
|
158
|
+
class_addMethod(cls, fromChange, impl, signature);
|
|
146
159
|
}
|
|
147
160
|
}
|
|
148
161
|
|
|
@@ -161,12 +174,26 @@ void pushwoosh_swizzle(Class class, SEL fromChange, SEL toChange, IMP impl, cons
|
|
|
161
174
|
|
|
162
175
|
#pragma mark - Pushwoosh RCTBridgeModule
|
|
163
176
|
|
|
164
|
-
|
|
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
|
|
165
186
|
|
|
166
187
|
- (dispatch_queue_t)methodQueue {
|
|
167
188
|
return dispatch_get_main_queue();
|
|
168
189
|
}
|
|
169
190
|
|
|
191
|
+
// Overriding init makes React Native infer main queue setup on its own; saying so keeps that
|
|
192
|
+
// contract explicit, since the module claims the notification center delegate right away.
|
|
193
|
+
+ (BOOL)requiresMainQueueSetup {
|
|
194
|
+
return YES;
|
|
195
|
+
}
|
|
196
|
+
|
|
170
197
|
+ (NSString *)getPluginImplementationInfoPlistKey {
|
|
171
198
|
return [[NSBundle mainBundle] objectForInfoDictionaryKey:kPushwooshPluginImplementationInfoPlistKey];
|
|
172
199
|
}
|
|
@@ -232,25 +259,43 @@ RCT_EXPORT_METHOD(init:(NSDictionary*)config success:(RCTResponseSenderBlock)suc
|
|
|
232
259
|
success(@[]);
|
|
233
260
|
}
|
|
234
261
|
|
|
235
|
-
|
|
236
|
-
|
|
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;
|
|
237
270
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
271
|
+
if (gStartPushData) {
|
|
272
|
+
NSURL *deepLink = pwplugin_deepLinkURLFromPushLink(gStartPushData[@"l"]);
|
|
273
|
+
|
|
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
|
+
}
|
|
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];
|
|
243
294
|
}
|
|
244
|
-
|
|
245
|
-
[self sendJSEvent:kPushReceivedJSEvent withArgs:gStartPushData];
|
|
246
|
-
[self sendJSEvent:kPushOpenJSEvent withArgs:gStartPushData];
|
|
247
|
-
} else if([PushNotificationManager pushManager].launchNotification) {
|
|
248
|
-
[self sendJSEvent:kPushReceivedJSEvent withArgs:[PushNotificationManager pushManager].launchNotification];
|
|
249
|
-
[self sendJSEvent:kPushOpenJSEvent withArgs:[PushNotificationManager pushManager].launchNotification];
|
|
250
295
|
}
|
|
251
296
|
}
|
|
252
297
|
|
|
253
|
-
RCT_EXPORT_METHOD(
|
|
298
|
+
RCT_EXPORT_METHOD(registerForPushNotifications:(RCTResponseSenderBlock)success error:(RCTResponseSenderBlock)error) {
|
|
254
299
|
[[PWEventDispatcher sharedDispatcher] subscribe:success toEvent:kRegistrationSuccesEvent];
|
|
255
300
|
[[PWEventDispatcher sharedDispatcher] subscribe:error toEvent:kRegistrationErrorEvent];
|
|
256
301
|
|
|
@@ -385,13 +430,13 @@ RCT_EXPORT_METHOD(setUserId:(NSString*)userId success:(RCTResponseSenderBlock)su
|
|
|
385
430
|
}];
|
|
386
431
|
}
|
|
387
432
|
|
|
388
|
-
RCT_EXPORT_METHOD(postEvent:(NSString*)event
|
|
433
|
+
RCT_EXPORT_METHOD(postEvent:(NSString*)event attributes:(NSDictionary*)attributes) {
|
|
389
434
|
[[PWInAppManager sharedManager] postEvent:event withAttributes:attributes];
|
|
390
435
|
}
|
|
391
436
|
|
|
392
|
-
RCT_EXPORT_METHOD(setApplicationIconBadgeNumber:(
|
|
437
|
+
RCT_EXPORT_METHOD(setApplicationIconBadgeNumber:(double)badgeNumber) {
|
|
393
438
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
394
|
-
[UIApplication sharedApplication].applicationIconBadgeNumber =
|
|
439
|
+
[UIApplication sharedApplication].applicationIconBadgeNumber = (NSInteger)badgeNumber;
|
|
395
440
|
});
|
|
396
441
|
}
|
|
397
442
|
|
|
@@ -407,9 +452,9 @@ RCT_EXPORT_METHOD(getApplicationIconBadgeNumber:(RCTResponseSenderBlock)callback
|
|
|
407
452
|
}
|
|
408
453
|
}
|
|
409
454
|
|
|
410
|
-
RCT_EXPORT_METHOD(addToApplicationIconBadgeNumber:(
|
|
455
|
+
RCT_EXPORT_METHOD(addToApplicationIconBadgeNumber:(double)badgeNumber) {
|
|
411
456
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
412
|
-
[UIApplication sharedApplication].applicationIconBadgeNumber +=
|
|
457
|
+
[UIApplication sharedApplication].applicationIconBadgeNumber += (NSInteger)badgeNumber;
|
|
413
458
|
});
|
|
414
459
|
}
|
|
415
460
|
|
|
@@ -423,7 +468,7 @@ RCT_EXPORT_METHOD(presentInboxUI:(NSDictionary *)styleDictionary) {
|
|
|
423
468
|
inboxViewController.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Close", @"Close") style:UIBarButtonItemStylePlain target:self action:@selector(closeInbox)];
|
|
424
469
|
[[PushwooshPlugin findRootViewController] presentViewController:[[UINavigationController alloc] initWithRootViewController:inboxViewController] animated:YES completion:nil];
|
|
425
470
|
|
|
426
|
-
__weak
|
|
471
|
+
__weak __typeof__(self) wself = self;
|
|
427
472
|
inboxViewController.onMessageClickBlock = ^(NSObject<PWInboxMessageProtocol> *message) {
|
|
428
473
|
if (message.type == PWInboxMessageTypeDeeplink) {
|
|
429
474
|
[wself closeInbox];
|
|
@@ -459,15 +504,19 @@ RCT_EXPORT_METHOD(messagesCount:(RCTResponseSenderBlock)callback) {
|
|
|
459
504
|
|
|
460
505
|
RCT_EXPORT_METHOD(loadMessages:(RCTResponseSenderBlock)success fail:(RCTResponseSenderBlock)fail) {
|
|
461
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
|
+
|
|
462
514
|
if (success) {
|
|
463
|
-
NSMutableArray*
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
success( @[ array ]);
|
|
469
|
-
} else if (error != nil && fail != nil) {
|
|
470
|
-
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 ]);
|
|
471
520
|
}
|
|
472
521
|
}];
|
|
473
522
|
}
|
|
@@ -559,25 +608,50 @@ API_AVAILABLE(ios(10.0)) {
|
|
|
559
608
|
NSString *currentHash = content.userInfo[@"p"];
|
|
560
609
|
BOOL isNeedToImplementInPlugin = [[PushwooshPlugin getPluginImplementationInfoPlistKey] boolValue];
|
|
561
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
|
+
|
|
562
615
|
if (isPushwooshMessage && isNeedToImplementInPlugin && ![lastPushwooshHash isEqualToString:currentHash]) {
|
|
563
616
|
lastPushwooshHash = currentHash;
|
|
564
617
|
|
|
565
618
|
if ([PushNotificationManager pushManager].showPushnotificationAlert) {
|
|
566
|
-
|
|
619
|
+
options = UNNotificationPresentationOptionSound | UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionAlert;
|
|
567
620
|
} else {
|
|
568
621
|
[[PushNotificationManager pushManager] handlePushReceived:content.userInfo];
|
|
569
622
|
}
|
|
570
623
|
|
|
571
624
|
[self sendJSEvent:kPushReceivedJSEvent withArgs:content.userInfo];
|
|
572
625
|
}
|
|
573
|
-
|
|
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.
|
|
574
648
|
if (_originalNotificationCenterDelegate != nil &&
|
|
575
649
|
_originalNotificationCenterDelegateResponds.willPresentNotification) {
|
|
576
650
|
|
|
577
651
|
dispatch_block_t presentationBlock = ^{
|
|
578
652
|
[self->_originalNotificationCenterDelegate userNotificationCenter:center
|
|
579
653
|
willPresentNotification:notification
|
|
580
|
-
withCompletionHandler:
|
|
654
|
+
withCompletionHandler:answerOnce];
|
|
581
655
|
};
|
|
582
656
|
|
|
583
657
|
if (isPushwooshMessage) {
|
|
@@ -585,14 +659,10 @@ API_AVAILABLE(ios(10.0)) {
|
|
|
585
659
|
} else {
|
|
586
660
|
presentationBlock();
|
|
587
661
|
}
|
|
662
|
+
return;
|
|
588
663
|
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
_originalNotificationCenterDelegateResponds.willPresentNotification) {
|
|
592
|
-
[_originalNotificationCenterDelegate userNotificationCenter:center
|
|
593
|
-
willPresentNotification:notification
|
|
594
|
-
withCompletionHandler:completionHandler];
|
|
595
|
-
}
|
|
664
|
+
|
|
665
|
+
answerOnce(options);
|
|
596
666
|
}
|
|
597
667
|
|
|
598
668
|
- (BOOL)isContentAvailablePush:(NSDictionary *)userInfo {
|
|
@@ -712,7 +782,8 @@ API_AVAILABLE(ios(10.0)) {
|
|
|
712
782
|
|
|
713
783
|
- (NSString*)dateToString:(NSDate*)date {
|
|
714
784
|
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
|
|
715
|
-
[formatter
|
|
785
|
+
[formatter setLocale:[NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]];
|
|
786
|
+
[formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZ"];
|
|
716
787
|
return [formatter stringFromDate:date];
|
|
717
788
|
}
|
|
718
789
|
|
|
@@ -954,6 +1025,25 @@ RCT_EXPORT_METHOD(enableHuaweiPushNotifications) {
|
|
|
954
1025
|
// available in Android only
|
|
955
1026
|
}
|
|
956
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
|
+
|
|
957
1047
|
RCT_EXPORT_METHOD(registerSMSNumber:(NSString *)phoneNumber) {
|
|
958
1048
|
[[Pushwoosh sharedInstance] registerSmsNumber:phoneNumber];
|
|
959
1049
|
}
|
|
@@ -962,8 +1052,8 @@ RCT_EXPORT_METHOD(registerWhatsappNumber:(NSString *)phoneNumber) {
|
|
|
962
1052
|
[[Pushwoosh sharedInstance] registerWhatsappNumber:phoneNumber];
|
|
963
1053
|
}
|
|
964
1054
|
|
|
965
|
-
RCT_EXPORT_METHOD(setRichMediaType:(
|
|
966
|
-
PWRichMediaPresentationStyle style =
|
|
1055
|
+
RCT_EXPORT_METHOD(setRichMediaType:(double)type) {
|
|
1056
|
+
PWRichMediaPresentationStyle style = (NSInteger)type == 0
|
|
967
1057
|
? PWRichMediaPresentationStyleModal
|
|
968
1058
|
: PWRichMediaPresentationStyleLegacy;
|
|
969
1059
|
[Pushwoosh.media setRichMediaPresentationStyle:style];
|
|
@@ -1000,9 +1090,15 @@ RCT_EXPORT_METHOD(getRichMediaType:(RCTResponseSenderBlock)callback) {
|
|
|
1000
1090
|
|
|
1001
1091
|
#pragma mark - RCTEventEmitter
|
|
1002
1092
|
|
|
1093
|
+
// RCTEventEmitter withholds events until JS subscribes through NativeEventEmitter, which is what
|
|
1094
|
+
// raises its listener count. The plugin's public API is DeviceEventEmitter, which never raises it,
|
|
1095
|
+
// so observation stays off and every event goes out.
|
|
1096
|
+
- (instancetype)init {
|
|
1097
|
+
return [super initWithDisabledObservation];
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1003
1100
|
- (void)sendJSEvent:(NSString*)event withArgs:(NSDictionary*)args {
|
|
1004
|
-
|
|
1005
|
-
[self.bridge.eventDispatcher sendDeviceEventWithName:event body:args];
|
|
1101
|
+
[self sendEventWithName:event body:args];
|
|
1006
1102
|
}
|
|
1007
1103
|
|
|
1008
1104
|
- (NSArray<NSString *> *)supportedEvents {
|
|
@@ -1021,6 +1117,7 @@ RCT_EXPORT_METHOD(getRichMediaType:(RCTResponseSenderBlock)callback) {
|
|
|
1021
1117
|
- (void)onPushReceived:(PushNotificationManager *)manager withNotification:(NSDictionary *)pushNotification onStart:(BOOL)onStart {
|
|
1022
1118
|
if (onStart) {
|
|
1023
1119
|
gStartPushData = pushNotification;
|
|
1120
|
+
gStartPushReplayed = NO;
|
|
1024
1121
|
// Save deep link URL for New Architecture (Linking.getInitialURL support)
|
|
1025
1122
|
NSURL *deepLink = pwplugin_deepLinkURLFromPushLink(pushNotification[@"l"]);
|
|
1026
1123
|
if (deepLink) {
|
|
@@ -1031,6 +1128,7 @@ RCT_EXPORT_METHOD(getRichMediaType:(RCTResponseSenderBlock)callback) {
|
|
|
1031
1128
|
- (void)onPushAccepted:(PushNotificationManager *)manager withNotification:(NSDictionary *)pushNotification onStart:(BOOL)onStart {
|
|
1032
1129
|
if (onStart) {
|
|
1033
1130
|
gStartPushData = pushNotification;
|
|
1131
|
+
gStartPushReplayed = NO;
|
|
1034
1132
|
// Save deep link URL for New Architecture (Linking.getInitialURL support)
|
|
1035
1133
|
NSURL *deepLink = pwplugin_deepLinkURLFromPushLink(pushNotification[@"l"]);
|
|
1036
1134
|
if (deepLink) {
|
|
@@ -1053,17 +1151,45 @@ API_AVAILABLE(ios(10.0))
|
|
|
1053
1151
|
@property (nonatomic, weak) id<UNUserNotificationCenterDelegate> originalDelegate;
|
|
1054
1152
|
@end
|
|
1055
1153
|
|
|
1154
|
+
// PLUGIN_NOTIFICATION_HANDLER suppresses the native delegate, and the plugin's own delegate is
|
|
1155
|
+
// installed in JS init — too late for a cold start, so nobody accepts the launch push. Both early
|
|
1156
|
+
// hooks below call this; the push hash keeps the second one from repeating it.
|
|
1157
|
+
static void pwplugin_acceptColdStartPushOnce(NSDictionary *userInfo) {
|
|
1158
|
+
if (![userInfo isKindOfClass:[NSDictionary class]]) {
|
|
1159
|
+
return;
|
|
1160
|
+
}
|
|
1161
|
+
if (![[[NSBundle mainBundle] objectForInfoDictionaryKey:kPushwooshPluginImplementationInfoPlistKey] boolValue]) {
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
1164
|
+
if (![PWMessage isPushwooshMessage:userInfo]) {
|
|
1165
|
+
return;
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
NSString *hash = userInfo[@"p"];
|
|
1169
|
+
if ([hash isKindOfClass:[NSString class]]) {
|
|
1170
|
+
if ([gEarlyHandledHash isEqualToString:hash]) {
|
|
1171
|
+
return;
|
|
1172
|
+
}
|
|
1173
|
+
gEarlyHandledHash = hash;
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
[[PushNotificationManager pushManager] handlePushAccepted:userInfo onStart:YES];
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1056
1179
|
@implementation PWEarlyNotificationDelegate
|
|
1057
1180
|
|
|
1058
1181
|
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
|
|
1059
1182
|
didReceiveNotificationResponse:(UNNotificationResponse *)response
|
|
1060
1183
|
withCompletionHandler:(void (^)(void))completionHandler API_AVAILABLE(ios(10.0)) {
|
|
1061
|
-
// Capture deep link from push notification on cold start
|
|
1062
1184
|
if ([response.notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
|
|
1063
1185
|
NSDictionary *userInfo = response.notification.request.content.userInfo;
|
|
1064
|
-
NSURL *
|
|
1065
|
-
|
|
1066
|
-
|
|
1186
|
+
NSURL *appSchemeDeepLink = pwplugin_deepLinkURLFromPushLink(userInfo[@"l"]);
|
|
1187
|
+
|
|
1188
|
+
if (appSchemeDeepLink) {
|
|
1189
|
+
// Routing it through the SDK too would deliver the deep link twice.
|
|
1190
|
+
gPushDeepLinkURL = appSchemeDeepLink;
|
|
1191
|
+
} else {
|
|
1192
|
+
pwplugin_acceptColdStartPushOnce(userInfo);
|
|
1067
1193
|
}
|
|
1068
1194
|
}
|
|
1069
1195
|
|
|
@@ -1123,8 +1249,12 @@ static PWEarlyNotificationDelegate *gEarlyDelegate = nil;
|
|
|
1123
1249
|
NSDictionary *remoteNotification = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
|
|
1124
1250
|
if (remoteNotification) {
|
|
1125
1251
|
NSURL *deepLink = pwplugin_deepLinkURLFromPushLink(remoteNotification[@"l"]);
|
|
1126
|
-
if (deepLink
|
|
1127
|
-
gPushDeepLinkURL
|
|
1252
|
+
if (deepLink) {
|
|
1253
|
+
if (!gPushDeepLinkURL) {
|
|
1254
|
+
gPushDeepLinkURL = deepLink;
|
|
1255
|
+
}
|
|
1256
|
+
} else {
|
|
1257
|
+
pwplugin_acceptColdStartPushOnce(remoteNotification);
|
|
1128
1258
|
}
|
|
1129
1259
|
}
|
|
1130
1260
|
}];
|
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');
|