doopush-react-native-sdk 0.1.2 → 0.5.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.
Files changed (39) hide show
  1. package/README.md +66 -34
  2. package/android/build.gradle +9 -7
  3. package/android/src/main/java/com/doopush/reactnative/DooPushReactNativeSDKModule.kt +185 -25
  4. package/build/DooPush.d.ts +44 -7
  5. package/build/DooPush.d.ts.map +1 -1
  6. package/build/DooPush.js +76 -1
  7. package/build/DooPush.js.map +1 -1
  8. package/build/events.d.ts +7 -4
  9. package/build/events.d.ts.map +1 -1
  10. package/build/events.js +5 -2
  11. package/build/events.js.map +1 -1
  12. package/build/hooks.d.ts +34 -0
  13. package/build/hooks.d.ts.map +1 -0
  14. package/build/hooks.js +138 -0
  15. package/build/hooks.js.map +1 -0
  16. package/build/index.d.ts +1 -0
  17. package/build/index.d.ts.map +1 -1
  18. package/build/index.js +1 -0
  19. package/build/index.js.map +1 -1
  20. package/build/types.d.ts +28 -1
  21. package/build/types.d.ts.map +1 -1
  22. package/build/types.js.map +1 -1
  23. package/ios/DooPushReactNativeSDK.podspec +1 -3
  24. package/ios/DooPushReactNativeSDKModule.swift +169 -4
  25. package/package.json +4 -1
  26. package/plugin/build/android/withAndroid.js +4 -0
  27. package/plugin/build/android/withAppBuildGradle.js +71 -30
  28. package/plugin/build/android/withGoogleServices.js +74 -12
  29. package/plugin/build/android/withGradleProperties.d.ts +3 -0
  30. package/plugin/build/android/withGradleProperties.js +26 -0
  31. package/plugin/build/android/withRootBuildGradle.d.ts +2 -2
  32. package/plugin/build/android/withRootBuildGradle.js +65 -10
  33. package/plugin/build/android/withSettingsGradle.d.ts +8 -0
  34. package/plugin/build/android/withSettingsGradle.js +33 -0
  35. package/plugin/build/ios/withIOS.js +2 -0
  36. package/plugin/build/ios/withPodfile.d.ts +8 -0
  37. package/plugin/build/ios/withPodfile.js +48 -0
  38. package/plugin/build/schema.d.ts +327 -1
  39. package/plugin/build/schema.js +82 -1
package/README.md CHANGED
@@ -1,48 +1,35 @@
1
1
  # DooPush React Native SDK
2
2
 
3
- > **v0.1.1 alpha** —— 最小 API surface,端到端真实可用。
4
- > 仅支持 FCM (Android) + APNs (iOS)。OEM 通道、React Hooks 在 v0.5.0 beta。
3
+ > React Native / Expo SDK,支持 APNs、FCM、Android 6 类 OEM 通道、Hooks、通知事件、WebSocket Gateway、角标、统计和第三方共存控制。
5
4
 
6
5
  [DooPush](https://doopush.com) 推送通知服务的 React Native SDK。基于 Expo Modules API 实现,可在 Expo(managed / prebuild)和 bare React Native 项目里使用。
7
6
 
8
- ## v0.1.x alpha 提供什么
7
+ ## 功能
9
8
 
10
9
  - ✅ `DooPush.configure(config)`
11
- - ✅ `DooPush.register()` —— iOS APNs / Android FCM 自动流程
12
- - ✅ `DooPush.registerWithToken(token, vendor)` —— 调用方已经有 token 时(如配合 expo-notifications 共存)
13
- - 🟡 `DooPush.getDeviceToken()` / `getDeviceId()` —— 当前都返回 `null`,原生 getter 还没暴露,v0.5.0 上线
14
- - ✅ 事件监听:`addRegisterListener`、`addRegisterErrorListener`、`addMessageListener`
15
- - ✅ Config 插件:FCM 厂商(google-services.json)、iOS entitlement
16
- - ✅ Android Active 模式:DooPush 接管通知 UI;与 `expo-notifications` 通过广播 relay 共存(opt-in,JS bridge 在 v0.5.0)
17
-
18
- ### v0.1 已知限制
19
-
20
- - `register()` 返回 `{token, deviceId, vendor}`,但 Android `deviceId` 当前是空字符串(服务端 deviceId 还没在 bridge 层捕获,v0.5.0 修)。`token` 和 `vendor` 字段是对的。
21
- - `getDeviceToken()` / `getDeviceId()` 在 Android 上一直返回 `null`(底层 SDK 还没公开 getter)。
22
-
23
- ## v0.1 不包含(v0.5.0+ 才有)
24
-
25
- - React Hooks(`useDooPush`、`useDooPushToken`)
26
- - OEM 通道(HMS / Honor / Xiaomi / OPPO / VIVO / Meizu)
27
- - WebSocket gateway 的 JS API
28
- - 统计 / 角标 / 通道相关的 JS API
29
- - npm 发布(当前用 git tag)
10
+ - ✅ `DooPush.register()` —— iOS APNs / Android FCM 或 OEM 自动流程
11
+ - ✅ `DooPush.registerWithToken(token, vendor)` —— 调用方已有 token 时的 passive 模式
12
+ - `DooPush.getDeviceToken()` / `getDeviceId()` / `getDeviceInfo()` —— iOS Android 均返回原生 SDK 缓存值
13
+ - ✅ `DooPush.updateDeviceInfo()` / `reportStatistics()` / `checkPermissionStatus()`
14
+ - ✅ 角标 API:`setBadge()` / `clearBadge()` / `getBadge()`
15
+ - ✅ React Hooks:`useDooPush()` / `useDooPushMessage()`
16
+ - ✅ 事件监听:注册、注册错误、消息、通知点击、通知打开、Gateway 状态
17
+ - 第三方共存控制:`setNotificationManagementMode`、`setExpoNotificationRelayEnabled`、`setNotificationDisplayEnabled`
18
+ - ✅ Config Plugin:iOS entitlement / background mode、Android FCM/HMS/Honor 配置文件、OEM 配置文件生成、Gradle plugin/dependency 注入和 manifest placeholders 合并
19
+ - Android OEM 通道:HMS / Honor / Xiaomi / OPPO / VIVO / Meizu
30
20
 
31
21
  ## 前置条件
32
22
 
33
- - iOS 原生 SDK ≥ **1.1.1**(SPM tag `v1.1.1` of `doopush-ios-sdk`,或路径方式本地引用)
34
- - Android 原生 SDK ≥ **1.1.0**(JitPack `com.github.doopush:doopush-android-sdk:v1.1.0`,或本地 mavenLocal
23
+ - iOS 原生 SDK ≥ **1.2.0**(monorepo 本地开发可路径引用未发布版本)
24
+ - Android 原生 SDK ≥ **1.2.0**(monorepo 本地开发可走 mavenLocal 用未发布版本)
35
25
  - Expo SDK 50+(或 RN 0.73+ bare)。**新项目推荐 Expo SDK 54+**
36
26
 
37
- ## 快速安装(公开发布后)
27
+ ## 快速安装
38
28
 
39
29
  ```bash
40
30
  npx expo install doopush-react-native-sdk
41
31
  ```
42
32
 
43
- > v0.1.x alpha **暂未发到 npm**,公开仓走 git tag:
44
- > `npm install github:doopush/doopush-react-native-sdk#v0.1.1`
45
-
46
33
  `app.json` 配 plugin:
47
34
 
48
35
  ```json
@@ -58,7 +45,15 @@ npx expo install doopush-react-native-sdk
58
45
  "ios": { "mode": "production" },
59
46
  "android": {
60
47
  "vendors": {
61
- "fcm": { "googleServicesFile": "./google-services.json" }
48
+ "fcm": { "googleServicesFile": "./google-services.json" },
49
+ "hms": { "agconnectServicesFile": "./agconnect-services.json" },
50
+ "honor": {
51
+ "mcsServicesFile": "./mcs-services.json"
52
+ },
53
+ "xiaomi": { "appId": "mi_app_id", "appKey": "mi_app_key" },
54
+ "oppo": { "appKey": "oppo_app_key", "appSecret": "oppo_app_secret" },
55
+ "vivo": { "appId": "vivo_app_id", "apiKey": "vivo_api_key" },
56
+ "meizu": { "appId": "meizu_app_id", "appKey": "meizu_app_key" }
62
57
  }
63
58
  }
64
59
  }
@@ -68,6 +63,8 @@ npx expo install doopush-react-native-sdk
68
63
  }
69
64
  ```
70
65
 
66
+ > OEM 配置支持两种方式:传 `servicesFile`/`mcsServicesFile`/`agconnectServicesFile` 复制厂商 JSON;或对 Xiaomi / OPPO / VIVO / Meizu / Honor 传内联凭证,prebuild 时会生成对应 `android/app/src/main/assets/*-services.json`。HMS 仍需 `agconnect-services.json` 文件。
67
+
71
68
  ```bash
72
69
  npx expo prebuild --clean
73
70
  npx expo run:android # 或 run:ios
@@ -76,10 +73,12 @@ npx expo run:android # 或 run:ios
76
73
  ## 用法
77
74
 
78
75
  ```tsx
79
- import { useEffect, useState } from 'react';
80
- import { DooPush, type DooPushMessage } from 'doopush-react-native-sdk';
76
+ import { useEffect } from 'react';
77
+ import { DooPush, useDooPush, type DooPushMessage } from 'doopush-react-native-sdk';
81
78
 
82
79
  export default function App() {
80
+ const { token, deviceId, register, lastMessage, error } = useDooPush();
81
+
83
82
  useEffect(() => {
84
83
  DooPush.configure({
85
84
  appId: 'your_app_id',
@@ -88,19 +87,32 @@ export default function App() {
88
87
  const sub = DooPush.addMessageListener((m: DooPushMessage) => {
89
88
  console.log('收到推送', m);
90
89
  });
91
- return () => sub.remove();
90
+ const clickSub = DooPush.addNotificationClickListener((m) => {
91
+ console.log('点击推送', m);
92
+ });
93
+ return () => { sub.remove(); clickSub.remove(); };
92
94
  }, []);
93
95
 
94
96
  const handleRegister = async () => {
95
97
  try {
96
- const { token, deviceId } = await DooPush.register();
98
+ const { token, deviceId } = await register();
97
99
  console.log('注册成功', token, deviceId);
98
100
  } catch (e) {
99
101
  console.error('注册失败', e);
100
102
  }
101
103
  };
102
104
 
103
- // ...
105
+ // 也可以直接使用 hook 暴露的状态:token / deviceId / lastMessage / error
106
+ console.log({ token, deviceId, lastMessage, error });
107
+ }
108
+
109
+ // 常用补充 API:
110
+ async function maintenance() {
111
+ await DooPush.setBadge(3);
112
+ await DooPush.clearBadge();
113
+ await DooPush.reportStatistics();
114
+ const permission = await DooPush.checkPermissionStatus();
115
+ console.log(permission);
104
116
  }
105
117
  ```
106
118
 
@@ -144,9 +156,13 @@ sdk/react-native/
144
156
  默认兼容。iOS 上 DooPush 接管 `UNUserNotificationCenterDelegate` 但走 delegate-forwarding,`expo-notifications` 的监听依然能收到。Android 上 DooPush 接管 `FirebaseMessagingService`,如果你也想让 `expo-notifications` 收到 FCM 消息,调(v0.5.0+):
145
157
 
146
158
  ```ts
159
+ DooPush.setNotificationManagementMode('active');
160
+ // relay 是独立开关;setNotificationManagementMode 不会覆盖该值。
147
161
  DooPush.setExpoNotificationRelayEnabled(true);
148
162
  ```
149
163
 
164
+ > `setNotificationDisplayEnabled` 仅控制 Android FCM 由 DooPush 自管展示通知的开关;iOS 端是 no-op。`setExpoNotificationRelayEnabled` 是独立开关,不会被 active/passive 模式切换重置。若 iOS 需要让位给其它通知库,请使用 `setNotificationManagementMode('passive')`。
165
+
150
166
  ### 与 `@react-native-firebase/messaging`
151
167
 
152
168
  **二选一** —— 两个库都声明 `FirebaseMessagingService`,manifest merger 只能留一个。如果你已经在用 `react-native-firebase`,就在 DooPush plugin 里**省略 fcm 厂商**,用 `react-native-firebase` 拿 token 后再交给 DooPush:
@@ -165,6 +181,22 @@ MIT
165
181
 
166
182
  ## CHANGELOG
167
183
 
184
+ ### v0.5.1
185
+ - **Fix (Android)**:`expo prebuild` 流程缺失 `google-services` Gradle 插件 classpath 注入,原生工程 sync 失败;config plugin 现在补齐 `withRootBuildGradle` / `withSettingsGradle` / `withGradleProperties`,prebuild 全流程跑通。
186
+ - **Fix (config plugin)**:`zod` 等 plugin 运行时依赖错误地放在 `devDependencies` 里,用户 `npx expo prebuild` 时 plugin 解析失败;改为生产依赖。
187
+ - **Fix (iOS)**:`normalizePermissionStatus` 在 `AsyncFunction` 闭包里少了 `self.` 前缀,调用 permission 相关 API 时崩溃;同步修复。
188
+ - **Dev**:example app 的 Podfile 在 monorepo 中可自动指向同仓库 `sdk/ios/DooPushSDK`,便于联动调试本地原生 SDK。
189
+ - **Install**:默认 npm dist-tag 改为 `latest`(不再要求 `@beta`),`npm install doopush-react-native-sdk` 直接安装最新版。
190
+
191
+ ### v0.5.0
192
+ - **能力补齐**:Android `register()` / `registerWithToken()` 返回真实 `deviceId`,`getDeviceToken()` / `getDeviceId()` / `getDeviceInfo()` 接入原生缓存。
193
+ - **新增 Hooks**:`useDooPush()`、`useDooPushMessage()`。
194
+ - **新增 API**:角标、权限状态、设备信息更新、统计上报。
195
+ - **新增事件**:通知点击、通知打开、Gateway open/closed/error;`connectGateway()` 在缺少 token 时会拒绝。
196
+ - **共存控制**:新增 active/passive 管理模式、Expo relay、通知展示开关。
197
+ - **Config Plugin**:扩展 Android OEM vendor 配置入口;支持内联凭证生成 services JSON,HMS/Honor Gradle plugin 注入,HMS/Honor services 文件同时复制到 app 根目录和 assets。
198
+ - **依赖底座**:iOS / Android 原生 SDK 对齐到 v1.2.0。
199
+
168
200
  ### v0.1.2
169
201
  - **chore**:发版流水线连通性测试(无功能变更)。验证 monorepo `sync-rn-sdk.yml` → `doopush-react-native-sdk` 公仓 → `auto-publish-release.yml` → GitHub Release + npm publish 全链路。dist-tag 应解析为 `alpha`(0.1.x ≤ 0.4.x)。
170
202
 
@@ -2,7 +2,7 @@ apply plugin: 'com.android.library'
2
2
  apply plugin: 'kotlin-android'
3
3
 
4
4
  group = 'com.doopush.reactnative'
5
- version = '0.1.0'
5
+ version = '0.5.0'
6
6
 
7
7
  def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
8
8
  apply from: expoModulesCorePlugin
@@ -18,15 +18,15 @@ android {
18
18
  minSdkVersion safeExtGet("minSdkVersion", 26)
19
19
  targetSdkVersion safeExtGet("targetSdkVersion", 34)
20
20
  versionCode 1
21
- versionName "0.1.0"
21
+ versionName "0.5.0"
22
22
  }
23
23
 
24
24
  compileOptions {
25
- sourceCompatibility JavaVersion.VERSION_1_8
26
- targetCompatibility JavaVersion.VERSION_1_8
25
+ sourceCompatibility JavaVersion.VERSION_17
26
+ targetCompatibility JavaVersion.VERSION_17
27
27
  }
28
28
  kotlinOptions {
29
- jvmTarget = '1.8'
29
+ jvmTarget = '17'
30
30
  }
31
31
  }
32
32
 
@@ -39,10 +39,12 @@ repositories {
39
39
  }
40
40
 
41
41
  dependencies {
42
- // DooPush native Android SDK v1.1.0+
42
+ // RN bridge directly uses NotificationManagerCompat / ContextCompat for permission checks.
43
+ implementation 'androidx.core:core-ktx:1.12.0'
44
+ // DooPush native Android SDK v1.2.0+
43
45
  // During v0.1 alpha dev: rely on mavenLocal (./gradlew :lib:publishToMavenLocal in sdk/android/DooPushSDK).
44
46
  // Once published, this resolves from JitPack via the maven{} block above.
45
- implementation 'com.github.doopush:doopush-android-sdk:1.1.+'
47
+ implementation 'com.doopush:android-sdk:1.2.0'
46
48
 
47
49
  // FCM (transitive via DooPush AAR's runtime deps for FCM, but explicit here for clarity).
48
50
  implementation platform('com.google.firebase:firebase-bom:32.7.0')
@@ -1,9 +1,15 @@
1
1
  package com.doopush.reactnative
2
2
 
3
+ import android.Manifest
4
+ import android.content.pm.PackageManager
5
+ import android.os.Build
6
+ import androidx.core.app.NotificationManagerCompat
7
+ import androidx.core.content.ContextCompat
3
8
  import com.doopush.sdk.DooPushCallback
4
9
  import com.doopush.sdk.DooPushManager
5
10
  import com.doopush.sdk.DooPushNotificationHandler
6
11
  import com.doopush.sdk.DooPushRegisterCallback
12
+ import com.doopush.sdk.DooPushRegisterResult
7
13
  import com.doopush.sdk.models.DooPushError
8
14
  import com.doopush.sdk.models.PushMessage
9
15
  import expo.modules.kotlin.Promise
@@ -12,18 +18,27 @@ import expo.modules.kotlin.modules.ModuleDefinition
12
18
 
13
19
  /**
14
20
  * DooPush React Native SDK — Android bridge
15
- * v0.1.0 alpha
21
+ * v0.5.0
16
22
  *
17
23
  * Mode: ACTIVE (default) — DooPush owns FCM display via DooPushFirebaseMessagingService.
18
- * Coexistence with expo-notifications/react-native-firebase is via the relay broadcast
19
- * (opt-in by host app calling setExpoNotificationRelayEnabled(true) from JS — not exposed in v0.1).
24
+ * Coexistence with expo-notifications/react-native-firebase is exposed through
25
+ * notification-management / relay APIs.
20
26
  */
21
27
  class DooPushReactNativeSDKModule : Module(), DooPushCallback {
22
28
 
23
29
  override fun definition() = ModuleDefinition {
24
30
  Name("DooPushReactNativeSDK")
25
31
 
26
- Events("onRegister", "onRegisterError", "onMessage")
32
+ Events(
33
+ "onRegister",
34
+ "onRegisterError",
35
+ "onMessage",
36
+ "onNotificationClick",
37
+ "onNotificationOpen",
38
+ "onGatewayOpen",
39
+ "onGatewayClosed",
40
+ "onGatewayError"
41
+ )
27
42
 
28
43
  OnCreate {
29
44
  DooPushManager.getInstance().setCallback(this@DooPushReactNativeSDKModule)
@@ -50,17 +65,20 @@ class DooPushReactNativeSDKModule : Module(), DooPushCallback {
50
65
  DooPushManager.getInstance().registerForPushNotifications(
51
66
  object : DooPushRegisterCallback {
52
67
  override fun onSuccess(token: String) {
68
+ // Compatibility fallback for older native SDK callback dispatch.
53
69
  promise.resolve(mapOf(
54
70
  "token" to token,
55
- "deviceId" to (DooPushManager.getInstance().run {
56
- // Try to read deviceId via reflection / public getter; v1.1.0 doesn't expose
57
- // a public getDeviceId(), so we fall back to empty string for v0.1.0.
58
- // TODO P3: expose getDeviceId() on Android Manager parity with iOS.
59
- ""
60
- }),
71
+ "deviceId" to (DooPushManager.getInstance().getDeviceId() ?: ""),
61
72
  "vendor" to currentVendor()
62
73
  ))
63
74
  }
75
+ override fun onSuccess(result: DooPushRegisterResult) {
76
+ promise.resolve(mapOf(
77
+ "token" to result.token,
78
+ "deviceId" to result.deviceId,
79
+ "vendor" to normalizeVendor(result.vendor)
80
+ ))
81
+ }
64
82
  override fun onError(error: DooPushError) {
65
83
  promise.reject("E_REGISTER", error.message ?: "register failed", null)
66
84
  }
@@ -75,7 +93,10 @@ class DooPushReactNativeSDKModule : Module(), DooPushCallback {
75
93
  vendor = vendor,
76
94
  callback = object : DooPushRegisterCallback {
77
95
  override fun onSuccess(t: String) {
78
- promise.resolve(mapOf("deviceId" to "")) // TODO P3: real deviceId
96
+ promise.resolve(mapOf("deviceId" to (DooPushManager.getInstance().getDeviceId() ?: "")))
97
+ }
98
+ override fun onSuccess(result: DooPushRegisterResult) {
99
+ promise.resolve(mapOf("deviceId" to result.deviceId))
79
100
  }
80
101
  override fun onError(error: DooPushError) {
81
102
  promise.reject("E_REGISTER", error.message ?: "register failed", null)
@@ -86,13 +107,97 @@ class DooPushReactNativeSDKModule : Module(), DooPushCallback {
86
107
 
87
108
  // ── token getters ───────────────────────────────────────────────
88
109
  AsyncFunction("getDeviceToken") { ->
89
- // Android SDK v1.1.0 doesn't expose a public getDeviceToken() — return null in v0.1.
90
- // TODO P3: add public getter on Android Manager and wire here.
91
- null as String?
110
+ DooPushManager.getInstance().getDeviceToken()
92
111
  }
93
112
 
94
113
  AsyncFunction("getDeviceId") { ->
95
- null as String? // same TODO
114
+ DooPushManager.getInstance().getDeviceId()
115
+ }
116
+
117
+ AsyncFunction("getDeviceInfo") { ->
118
+ DooPushManager.getInstance().getDeviceInfo()?.let { deviceInfo ->
119
+ mapOf(
120
+ "platform" to deviceInfo.platform,
121
+ "channel" to normalizeVendor(deviceInfo.channel),
122
+ "bundleId" to deviceInfo.bundleId,
123
+ "brand" to deviceInfo.brand,
124
+ "model" to deviceInfo.model,
125
+ "systemVersion" to deviceInfo.systemVersion,
126
+ "appVersion" to deviceInfo.appVersion,
127
+ "userAgent" to deviceInfo.userAgent
128
+ )
129
+ }
130
+ }
131
+
132
+ AsyncFunction("updateDeviceInfo") { promise: Promise ->
133
+ DooPushManager.getInstance().updateDeviceInfo { success, error ->
134
+ if (success) {
135
+ promise.resolve(null)
136
+ } else {
137
+ promise.reject("E_UPDATE_DEVICE_INFO", error?.message ?: "updateDeviceInfo failed", null)
138
+ }
139
+ }
140
+ }
141
+
142
+ AsyncFunction("reportStatistics") { ->
143
+ DooPushManager.getInstance().reportStatistics()
144
+ }
145
+
146
+ AsyncFunction("checkPermissionStatus") { ->
147
+ checkPermissionStatus()
148
+ }
149
+
150
+ AsyncFunction("setBadge") { count: Int, promise: Promise ->
151
+ if (count < 0) {
152
+ promise.reject("E_BADGE", "badge count must be >= 0", null)
153
+ } else {
154
+ promise.resolve(DooPushManager.getInstance().setBadgeCount(count))
155
+ }
156
+ }
157
+
158
+ AsyncFunction("clearBadge") { ->
159
+ DooPushManager.getInstance().clearBadge()
160
+ }
161
+
162
+ AsyncFunction("getBadge") { ->
163
+ DooPushManager.getInstance().getBadgeCount()
164
+ }
165
+
166
+ // ── notification management / coexistence ───────────────────────
167
+ Function("setNotificationManagementMode") { mode: String ->
168
+ val manager = DooPushManager.getInstance()
169
+ val resolved = when (mode.lowercase()) {
170
+ "active" -> DooPushManager.NotificationManagementMode.ACTIVE
171
+ "passive" -> DooPushManager.NotificationManagementMode.PASSIVE
172
+ else -> throw IllegalArgumentException("mode must be 'active' or 'passive'")
173
+ }
174
+ manager.setNotificationManagementMode(resolved)
175
+ // Keep relay independent: callers may opt into Expo relay in either mode.
176
+ manager.setFCMNotificationDisplayEnabled(
177
+ resolved == DooPushManager.NotificationManagementMode.ACTIVE
178
+ )
179
+ }
180
+
181
+ Function("setExpoNotificationRelayEnabled") { enabled: Boolean ->
182
+ DooPushManager.getInstance().setExpoNotificationRelayEnabled(enabled)
183
+ }
184
+
185
+ Function("setNotificationDisplayEnabled") { enabled: Boolean ->
186
+ DooPushManager.getInstance().setFCMNotificationDisplayEnabled(enabled)
187
+ }
188
+
189
+ AsyncFunction("connectGateway") { promise: Promise ->
190
+ if (DooPushManager.getInstance().getDeviceToken().isNullOrBlank()) {
191
+ promise.reject("E_GATEWAY", "device token is required before connecting gateway", null)
192
+ } else {
193
+ DooPushManager.getInstance().connectWebSocket()
194
+ promise.resolve(null)
195
+ }
196
+ }
197
+
198
+ AsyncFunction("disconnectGateway") { promise: Promise ->
199
+ DooPushManager.getInstance().disconnectWebSocket()
200
+ promise.resolve(null)
96
201
  }
97
202
  }
98
203
 
@@ -101,11 +206,19 @@ class DooPushReactNativeSDKModule : Module(), DooPushCallback {
101
206
  override fun onRegisterSuccess(token: String) {
102
207
  sendEvent("onRegister", mapOf(
103
208
  "token" to token,
104
- "deviceId" to "", // TODO P3
209
+ "deviceId" to (DooPushManager.getInstance().getDeviceId() ?: ""),
105
210
  "vendor" to currentVendor()
106
211
  ))
107
212
  }
108
213
 
214
+ override fun onRegisterSuccess(result: DooPushRegisterResult) {
215
+ sendEvent("onRegister", mapOf(
216
+ "token" to result.token,
217
+ "deviceId" to result.deviceId,
218
+ "vendor" to normalizeVendor(result.vendor)
219
+ ))
220
+ }
221
+
109
222
  override fun onRegisterError(error: DooPushError) {
110
223
  sendEvent("onRegisterError", mapOf(
111
224
  "code" to "E_REGISTER",
@@ -125,22 +238,69 @@ class DooPushReactNativeSDKModule : Module(), DooPushCallback {
125
238
  ))
126
239
  }
127
240
 
128
- // The DooPushCallback interface (v1.1.0) has additional non-default abstract methods
129
- // that the plan code didn't list. Implement them as no-ops for v0.1.0 — surfacing
130
- // direct token-fetch results to JS isn't part of the v0.1 surface (the JS layer only
131
- // consumes onRegister/onRegisterError/onMessage). v0.5.0 may surface these.
241
+ // Direct token-fetch callbacks are not part of the RN v0.5.0 surface; apps consume
242
+ // register/registerWithToken plus onRegister/onRegisterError/onMessage events.
132
243
  override fun onTokenReceived(token: String) {
133
- // No-op: not surfaced in v0.1.0.
244
+ // No-op.
134
245
  }
135
246
 
136
247
  override fun onTokenError(error: DooPushError) {
137
- // No-op: not surfaced in v0.1.0.
248
+ // No-op.
138
249
  }
139
250
 
140
251
  override fun onNotificationClick(notificationData: DooPushNotificationHandler.NotificationData) {
141
- // v0.5.0 will surface this as onNotificationClick. For v0.1, ignored.
252
+ sendEvent("onNotificationClick", normalizeNotification(notificationData))
253
+ }
254
+
255
+ override fun onNotificationOpen(notificationData: DooPushNotificationHandler.NotificationData) {
256
+ sendEvent("onNotificationOpen", normalizeNotification(notificationData))
257
+ }
258
+
259
+ override fun onWebSocketOpen() {
260
+ sendEvent("onGatewayOpen", mapOf("connected" to true))
261
+ }
262
+
263
+ override fun onWebSocketClosed(code: Int, reason: String) {
264
+ sendEvent("onGatewayClosed", mapOf("code" to code, "reason" to reason))
265
+ }
266
+
267
+ override fun onWebSocketFailure(t: Throwable) {
268
+ sendEvent("onGatewayError", mapOf(
269
+ "code" to "E_GATEWAY",
270
+ "message" to (t.message ?: "WebSocket failure")
271
+ ))
272
+ }
273
+
274
+ private fun normalizeNotification(
275
+ notificationData: DooPushNotificationHandler.NotificationData
276
+ ): Map<String, Any?> = mapOf(
277
+ "vendor" to currentVendor(),
278
+ "title" to notificationData.title,
279
+ "body" to notificationData.body,
280
+ "pushLogId" to notificationData.pushLogId,
281
+ "dedupKey" to notificationData.dedupKey,
282
+ "data" to notificationData.payload.mapValues { it.value.toString() }
283
+ )
284
+
285
+ private fun currentVendor(): String =
286
+ normalizeVendor(DooPushManager.getInstance().getCurrentVendor() ?: "fcm")
287
+
288
+ private fun checkPermissionStatus(): String {
289
+ val context = appContext.reactContext ?: return "unknown"
290
+ val notificationsEnabled = NotificationManagerCompat.from(context).areNotificationsEnabled()
291
+ if (!notificationsEnabled) return "denied"
292
+
293
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
294
+ val granted = ContextCompat.checkSelfPermission(
295
+ context,
296
+ Manifest.permission.POST_NOTIFICATIONS
297
+ ) == PackageManager.PERMISSION_GRANTED
298
+ return if (granted) "authorized" else "denied"
299
+ }
300
+
301
+ return "authorized"
142
302
  }
143
303
 
144
- /** Best-effort vendor inference for v0.1 (default fcm). v0.5.0 will read from active vendor state. */
145
- private fun currentVendor(): String = "fcm"
304
+ private fun normalizeVendor(vendor: String): String =
305
+ if (vendor == "huawei") "hms" else vendor
146
306
  }
@@ -1,10 +1,11 @@
1
1
  import { Subscription } from 'expo-modules-core';
2
- import type { DooPushConfig, DooPushVendor, DooPushMessage } from './types';
2
+ import type { DooPushConfig, DooPushVendor, DooPushMessage, DooPushNotificationManagementMode, DooPushGatewayOpenEvent, DooPushGatewayClosedEvent, DooPushGatewayErrorEvent, DooPushRegisterResult, DooPushDeviceInfo, DooPushPermissionStatus } from './types';
3
3
  import type { OnRegisterEvent, OnRegisterErrorEvent } from './events';
4
4
  /**
5
5
  * DooPush React Native SDK — imperative API
6
6
  *
7
- * v0.1.0 alpha minimal surface: configure / register / token getters / event listeners
7
+ * v0.5.0 — registration, OEM channels, token/device getters, hooks,
8
+ * notification events, WebSocket gateway, badge, statistics, and coexistence controls.
8
9
  */
9
10
  /**
10
11
  * Configure the SDK. Call once per app launch, ideally in App entry point.
@@ -18,11 +19,7 @@ export declare function configure(config: DooPushConfig): void;
18
19
  * Returns the device token + DooPush deviceId on success.
19
20
  * Rejects if user denies permission or network fails.
20
21
  */
21
- export declare function register(): Promise<{
22
- token: string;
23
- deviceId: string;
24
- vendor: DooPushVendor;
25
- }>;
22
+ export declare function register(): Promise<DooPushRegisterResult>;
26
23
  /**
27
24
  * Register with a token already obtained externally (e.g. via expo-notifications
28
25
  * `getDevicePushTokenAsync()`). Skips SDK's internal permission/token flow.
@@ -34,7 +31,47 @@ export declare function registerWithToken(token: string, vendor?: DooPushVendor)
34
31
  export declare function getDeviceToken(): Promise<string | null>;
35
32
  /** Returns DooPush server-assigned deviceId (after register success), or null. */
36
33
  export declare function getDeviceId(): Promise<string | null>;
34
+ /** Returns the current native SDK device information snapshot, or null before configure. */
35
+ export declare function getDeviceInfo(): Promise<DooPushDeviceInfo | null>;
36
+ /** Refresh device information on the DooPush server using the current token. */
37
+ export declare function updateDeviceInfo(): Promise<void>;
38
+ /** Immediately flush locally collected push statistics. */
39
+ export declare function reportStatistics(): Promise<void>;
40
+ /** Check OS-level push notification permission status. */
41
+ export declare function checkPermissionStatus(): Promise<DooPushPermissionStatus>;
42
+ /** Set the application badge count. */
43
+ export declare function setBadge(count: number): Promise<boolean>;
44
+ /** Clear the application badge count. */
45
+ export declare function clearBadge(): Promise<boolean>;
46
+ /** Return the last known application badge count. */
47
+ export declare function getBadge(): Promise<number>;
48
+ /**
49
+ * Set notification management mode.
50
+ *
51
+ * - active: DooPush requests permission / owns notification handling where possible.
52
+ * - passive: host app obtains token and calls registerWithToken().
53
+ */
54
+ export declare function setNotificationManagementMode(mode: DooPushNotificationManagementMode): void;
55
+ /**
56
+ * Android: relay FCM messages to Expo Notifications coexistence broadcast.
57
+ * iOS: no-op because delegate forwarding is used.
58
+ */
59
+ export declare function setExpoNotificationRelayEnabled(enabled: boolean): void;
60
+ /**
61
+ * Android: toggle SDK-owned FCM notification display.
62
+ * iOS: no-op; use setNotificationManagementMode('passive') to disable DooPush delegate tracking.
63
+ */
64
+ export declare function setNotificationDisplayEnabled(enabled: boolean): void;
65
+ /** Manually connect the WebSocket gateway using the registered device token. */
66
+ export declare function connectGateway(): Promise<void>;
67
+ /** Manually disconnect the WebSocket gateway. */
68
+ export declare function disconnectGateway(): Promise<void>;
37
69
  export declare function addRegisterListener(listener: (e: OnRegisterEvent) => void): Subscription;
38
70
  export declare function addRegisterErrorListener(listener: (e: OnRegisterErrorEvent) => void): Subscription;
39
71
  export declare function addMessageListener(listener: (msg: DooPushMessage) => void): Subscription;
72
+ export declare function addNotificationClickListener(listener: (msg: DooPushMessage) => void): Subscription;
73
+ export declare function addNotificationOpenListener(listener: (msg: DooPushMessage) => void): Subscription;
74
+ export declare function addGatewayOpenListener(listener: (e: DooPushGatewayOpenEvent) => void): Subscription;
75
+ export declare function addGatewayClosedListener(listener: (e: DooPushGatewayClosedEvent) => void): Subscription;
76
+ export declare function addGatewayErrorListener(listener: (e: DooPushGatewayErrorEvent) => void): Subscription;
40
77
  //# sourceMappingURL=DooPush.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DooPush.d.ts","sourceRoot":"","sources":["../src/DooPush.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,KAAK,EACV,aAAa,EACb,aAAa,EACb,cAAc,EACf,MAAM,SAAS,CAAC;AACjB,OAAO,KAAK,EACV,eAAe,EACf,oBAAoB,EACrB,MAAM,UAAU,CAAC;AAElB;;;;GAIG;AAEH;;;GAGG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAErD;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,IAAI,OAAO,CAAC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,aAAa,CAAC;CACvB,CAAC,CAED;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,EACb,MAAM,GAAE,aAAqB,GAC5B,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAE/B;AAED,sEAAsE;AACtE,wBAAgB,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAEvD;AAED,kFAAkF;AAClF,wBAAgB,WAAW,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAEpD;AAID,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,CAAC,CAAC,EAAE,eAAe,KAAK,IAAI,GACrC,YAAY,CAEd;AAED,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,CAAC,CAAC,EAAE,oBAAoB,KAAK,IAAI,GAC1C,YAAY,CAEd;AAED,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,GACtC,YAAY,CAEd"}
1
+ {"version":3,"file":"DooPush.d.ts","sourceRoot":"","sources":["../src/DooPush.ts"],"names":[],"mappings":"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"}