react-native-push-signal 0.1.9 → 0.1.11

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 (36) hide show
  1. package/README.md +49 -2
  2. package/README.ru.md +49 -2
  3. package/android/src/main/AndroidManifest.xml +7 -0
  4. package/android/src/main/java/com/pushsignal/PushModels.kt +23 -0
  5. package/android/src/main/java/com/pushsignal/PushSignalCenter.kt +151 -17
  6. package/android/src/main/java/com/pushsignal/PushSignalModule.kt +30 -3
  7. package/ios/PushSignal.mm +18 -0
  8. package/ios/PushSignalCenter.m +0 -1
  9. package/lib/module/NativePushSignal.js.map +1 -1
  10. package/lib/module/PushSignalError.js +29 -0
  11. package/lib/module/PushSignalError.js.map +1 -0
  12. package/lib/module/index.js +2 -1
  13. package/lib/module/index.js.map +1 -1
  14. package/lib/module/pushSignal.js +3 -0
  15. package/lib/module/pushSignal.js.map +1 -1
  16. package/lib/module/pushSignal.native.js +51 -5
  17. package/lib/module/pushSignal.native.js.map +1 -1
  18. package/lib/typescript/src/NativePushSignal.d.ts +19 -1
  19. package/lib/typescript/src/NativePushSignal.d.ts.map +1 -1
  20. package/lib/typescript/src/PushSignalError.d.ts +22 -0
  21. package/lib/typescript/src/PushSignalError.d.ts.map +1 -0
  22. package/lib/typescript/src/index.d.ts +3 -2
  23. package/lib/typescript/src/index.d.ts.map +1 -1
  24. package/lib/typescript/src/pushSignal.d.ts +2 -1
  25. package/lib/typescript/src/pushSignal.d.ts.map +1 -1
  26. package/lib/typescript/src/pushSignal.native.d.ts +2 -1
  27. package/lib/typescript/src/pushSignal.native.d.ts.map +1 -1
  28. package/lib/typescript/src/types.d.ts +18 -1
  29. package/lib/typescript/src/types.d.ts.map +1 -1
  30. package/package.json +1 -1
  31. package/src/NativePushSignal.ts +20 -1
  32. package/src/PushSignalError.ts +29 -0
  33. package/src/index.tsx +4 -0
  34. package/src/pushSignal.native.tsx +68 -6
  35. package/src/pushSignal.tsx +5 -0
  36. package/src/types.ts +28 -1
package/README.md CHANGED
@@ -38,7 +38,7 @@ if (status !== RESULTS.GRANTED) {
38
38
  }
39
39
 
40
40
  const credentials = await getCredentials();
41
- // POST credentials to your server: { platform, token, environment? }
41
+ // POST credentials to your server: { token, environment? }
42
42
 
43
43
  const stopMessages = onMessage((message) => {
44
44
  console.log('incoming', message);
@@ -115,7 +115,7 @@ If `getCredentials()` throws `Failed to get an FCM token: SERVICE_NOT_AVAILABLE`
115
115
  Before requesting the token the library checks Google Play services and retries a few times with backoff, but some causes can only be fixed on the device:
116
116
 
117
117
  - **Google account.** The device must be signed into a Google account. FCM does not issue a token without one.
118
- - **Google Play services.** They must be installed, enabled and up to date. On a China-only ROM without GMS, FCM cannot work at all — you need another push provider.
118
+ - **Google Play services.** They must be installed, enabled and up to date. On a China-only ROM without GMS, FCM cannot work at all — you need another push provider; see [Detecting a non-Google device](#detecting-a-non-google-device-getdiagnostics-and-pushsignalerror).
119
119
  - **Network.** Check connectivity and disable VPN and private DNS.
120
120
  - **MIUI / HyperOS.** Enable autostart and remove battery restrictions: Settings → Apps → your app → Autostart; Battery → No restrictions. Allow background data for the app.
121
121
 
@@ -125,6 +125,53 @@ What to look for in the logs:
125
125
  adb logcat | grep -iE "SERVICE_NOT_AVAILABLE|FirebaseMessaging|PushSignal"
126
126
  ```
127
127
 
128
+ ### Detecting a non-Google device: `getDiagnostics()` and `PushSignalError`
129
+
130
+ When Google Play services cannot serve FCM, the library detects it and tells you which service to use instead — both in the error message and as structured data, so the cause is visible in React Native logs.
131
+
132
+ `initialize()` and `getCredentials()` reject with `PushSignalError`:
133
+
134
+ ```ts
135
+ import { getCredentials, PushSignalError } from 'react-native-push-signal';
136
+
137
+ try {
138
+ const credentials = await getCredentials();
139
+ } catch (error) {
140
+ if (error instanceof PushSignalError) {
141
+ console.warn(error.code); // E_GMS_MISSING, E_FCM_TOKEN, ...
142
+ console.warn(error.message); // "... Use HMS Push Kit (Huawei Push) instead of the standard Google service."
143
+ console.warn(error.provider); // 'hms'
144
+ console.warn(error.hint); // actionable sentence
145
+ console.warn(error.diagnostics); // full device/provider snapshot
146
+ }
147
+ }
148
+ ```
149
+
150
+ You can also inspect diagnostics without requesting a token:
151
+
152
+ ```ts
153
+ import { getDiagnostics } from 'react-native-push-signal';
154
+
155
+ const diagnostics = await getDiagnostics();
156
+ // { platform, gmsAvailable, gmsStatus, manufacturer, brand, model,
157
+ // provider, providerName, providerInstalled?, hint? }
158
+ ```
159
+
160
+ Provider suggested when GMS is unavailable:
161
+
162
+ | Manufacturer | `provider` | Service |
163
+ | --------------------------- | ------------ | --------------------------------------- |
164
+ | HUAWEI / HONOR | `hms` | HMS Push Kit (Huawei Push) |
165
+ | Xiaomi / Redmi / POCO | `mi_push` | Mi Push (Xiaomi Push) |
166
+ | OPPO / OnePlus / realme | `oppo_push` | OPPO Push (HeyTap) |
167
+ | vivo / iQOO | `vivo_push` | vivo Push |
168
+ | Meizu | `meizu_push` | Meizu Push |
169
+ | anything else | `unknown` | generic “use another provider” |
170
+
171
+ `providerInstalled` is `true` only when the provider service app was positively found on the device (`com.huawei.hwid`, `com.xiaomi.xmsf`); otherwise it is omitted. iOS always reports `provider: 'apns'`. The library never logs for you — pass the data wherever you need.
172
+
173
+ Error codes: `E_GMS_MISSING`, `E_GMS_DISABLED`, `E_GMS_UPDATE_REQUIRED`, `E_GMS_INVALID`, `E_GMS_UNAVAILABLE`, `E_FCM_TOKEN`, `E_FIREBASE_CONFIG`, `E_NOT_INITIALIZED`.
174
+
128
175
  ## Incoming messages vs taps
129
176
 
130
177
  | App state | iOS | Android (notification payload) | Android (data-only) |
package/README.ru.md CHANGED
@@ -38,7 +38,7 @@ if (status !== RESULTS.GRANTED) {
38
38
  }
39
39
 
40
40
  const credentials = await getCredentials();
41
- // POST credentials на сервер: { platform, token, environment? }
41
+ // POST credentials на сервер: { token, environment? }
42
42
 
43
43
  const stopMessages = onMessage((message) => {
44
44
  console.log('incoming', message);
@@ -115,7 +115,7 @@ apply plugin: "com.google.gms.google-services"
115
115
  Перед запросом токена библиотека проверяет Google Play services и делает несколько попыток с паузами, но часть причин лечится только на устройстве:
116
116
 
117
117
  - **Google-аккаунт.** На устройстве должен быть выполнен вход в Google-аккаунт. Без него FCM токен не выдаёт.
118
- - **Google Play services.** Должны быть установлены, включены и обновлены. На китайской прошивке без GMS FCM не заработает в принципе — нужен другой провайдер пушей.
118
+ - **Google Play services.** Должны быть установлены, включены и обновлены. На китайской прошивке без GMS FCM не заработает в принципе — нужен другой провайдер пушей; см. [Определение не‑Google устройства](#определение-неgoogle-устройства-getdiagnostics-и-pushsignalerror).
119
119
  - **Сеть.** Проверьте интернет, отключите VPN и приватный DNS.
120
120
  - **MIUI / HyperOS.** Включите автозапуск и снимите ограничения батареи: Настройки → Приложения → «ваше приложение» → Автозапуск; Батарея → Без ограничений. Разрешите приложению фоновые данные.
121
121
 
@@ -125,6 +125,53 @@ apply plugin: "com.google.gms.google-services"
125
125
  adb logcat | grep -iE "SERVICE_NOT_AVAILABLE|FirebaseMessaging|PushSignal"
126
126
  ```
127
127
 
128
+ ### Определение не‑Google устройства: `getDiagnostics()` и `PushSignalError`
129
+
130
+ Когда Google Play services не могут обслуживать FCM, библиотека это определяет и подсказывает, какой сервис использовать вместо стандартного — в тексте ошибки и в структурированном виде, чтобы причина была видна в логах React Native.
131
+
132
+ `initialize()` и `getCredentials()` отклоняются с `PushSignalError`:
133
+
134
+ ```ts
135
+ import { getCredentials, PushSignalError } from 'react-native-push-signal';
136
+
137
+ try {
138
+ const credentials = await getCredentials();
139
+ } catch (error) {
140
+ if (error instanceof PushSignalError) {
141
+ console.warn(error.code); // E_GMS_MISSING, E_FCM_TOKEN, ...
142
+ console.warn(error.message); // "... Use HMS Push Kit (Huawei Push) instead of the standard Google service."
143
+ console.warn(error.provider); // 'hms'
144
+ console.warn(error.hint); // готовая подсказка
145
+ console.warn(error.diagnostics); // полный снимок устройства и провайдера
146
+ }
147
+ }
148
+ ```
149
+
150
+ Диагностику можно получить и без запроса токена:
151
+
152
+ ```ts
153
+ import { getDiagnostics } from 'react-native-push-signal';
154
+
155
+ const diagnostics = await getDiagnostics();
156
+ // { platform, gmsAvailable, gmsStatus, manufacturer, brand, model,
157
+ // provider, providerName, providerInstalled?, hint? }
158
+ ```
159
+
160
+ Провайдер, который предлагается при недоступных GMS:
161
+
162
+ | Производитель | `provider` | Сервис |
163
+ | ------------------------ | ------------ | ---------------------------------- |
164
+ | HUAWEI / HONOR | `hms` | HMS Push Kit (Huawei Push) |
165
+ | Xiaomi / Redmi / POCO | `mi_push` | Mi Push (Xiaomi Push) |
166
+ | OPPO / OnePlus / realme | `oppo_push` | OPPO Push (HeyTap) |
167
+ | vivo / iQOO | `vivo_push` | vivo Push |
168
+ | Meizu | `meizu_push` | Meizu Push |
169
+ | остальные | `unknown` | общая подсказка «нужен другой» |
170
+
171
+ `providerInstalled` равно `true` только если сервис-приложение провайдера действительно найдено на устройстве (`com.huawei.hwid`, `com.xiaomi.xmsf`), иначе поле отсутствует. iOS всегда сообщает `provider: 'apns'`. Библиотека сама ничего не логирует — передавайте данные туда, куда нужно.
172
+
173
+ Коды ошибок: `E_GMS_MISSING`, `E_GMS_DISABLED`, `E_GMS_UPDATE_REQUIRED`, `E_GMS_INVALID`, `E_GMS_UNAVAILABLE`, `E_FCM_TOKEN`, `E_FIREBASE_CONFIG`, `E_NOT_INITIALIZED`.
174
+
128
175
  ## Входящее сообщение и тап
129
176
 
130
177
  | Состояние | iOS | Android (notification payload) | Android (data-only) |
@@ -1,6 +1,13 @@
1
1
  <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
2
  <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
3
3
 
4
+ <!-- Android 11+ package visibility: lets isPackageInstalled() confirm an
5
+ alternative push provider on the device. -->
6
+ <queries>
7
+ <package android:name="com.huawei.hwid" />
8
+ <package android:name="com.xiaomi.xmsf" />
9
+ </queries>
10
+
4
11
  <application>
5
12
  <provider
6
13
  android:name="com.pushsignal.PushSignalInitProvider"
@@ -13,3 +13,26 @@ data class AndroidFirebaseConfig(
13
13
  val current_key: String?,
14
14
  val project_number: String?,
15
15
  )
16
+
17
+ data class PushDiagnostics(
18
+ val platform: String,
19
+ val gmsAvailable: Boolean,
20
+ val gmsStatus: Int,
21
+ val manufacturer: String,
22
+ val brand: String,
23
+ val model: String,
24
+ val provider: String,
25
+ val providerName: String,
26
+ val providerInstalled: Boolean?,
27
+ val hint: String?,
28
+ )
29
+
30
+ /**
31
+ * Carries a machine-readable [code] to JavaScript, so the host app can branch on
32
+ * the exact reason (missing GMS, disabled GMS, FCM token failure, ...).
33
+ */
34
+ class PushSignalException(
35
+ val code: String,
36
+ message: String,
37
+ cause: Throwable? = null,
38
+ ) : IllegalStateException(message, cause)
@@ -7,6 +7,7 @@ import android.app.NotificationManager
7
7
  import android.app.PendingIntent
8
8
  import android.content.Context
9
9
  import android.content.Intent
10
+ import android.content.pm.PackageManager
10
11
  import android.os.Build
11
12
  import android.os.Bundle
12
13
  import android.os.Handler
@@ -36,6 +37,18 @@ internal object PushSignalCenter : Application.ActivityLifecycleCallbacks {
36
37
  private const val TOKEN_INITIAL_RETRY_DELAY_MS = 1_000L
37
38
  private const val TOKEN_MAX_RETRY_DELAY_MS = 8_000L
38
39
 
40
+ private const val PROVIDER_FCM = "fcm"
41
+ private const val PROVIDER_HMS = "hms"
42
+ private const val PROVIDER_MI_PUSH = "mi_push"
43
+ private const val PROVIDER_OPPO_PUSH = "oppo_push"
44
+ private const val PROVIDER_VIVO_PUSH = "vivo_push"
45
+ private const val PROVIDER_MEIZU_PUSH = "meizu_push"
46
+ private const val PROVIDER_UNKNOWN = "unknown"
47
+
48
+ /** Service apps used to positively confirm an alternative provider on the device. */
49
+ private const val PACKAGE_HMS = "com.huawei.hwid"
50
+ private const val PACKAGE_MI_PUSH = "com.xiaomi.xmsf"
51
+
39
52
  private val lock = Any()
40
53
  private val mainHandler = Handler(Looper.getMainLooper())
41
54
  @Volatile private var application: Application? = null
@@ -107,7 +120,7 @@ internal object PushSignalCenter : Application.ActivityLifecycleCallbacks {
107
120
 
108
121
  fun fetchToken(): String {
109
122
  val context = application
110
- ?: throw IllegalStateException("PushSignal is not initialized")
123
+ ?: throw PushSignalException("E_NOT_INITIALIZED", "PushSignal is not initialized")
111
124
 
112
125
  ensurePlayServices(context)
113
126
 
@@ -117,16 +130,17 @@ internal object PushSignalCenter : Application.ActivityLifecycleCallbacks {
117
130
  }
118
131
  FirebaseApp.getInstance()
119
132
  } catch (error: IllegalStateException) {
120
- throw IllegalStateException(
133
+ throw PushSignalException(
134
+ "E_FIREBASE_CONFIG",
121
135
  "Firebase is not configured. Call initialize({ project_id, mobilesdk_app_id, current_key, project_number }) or add google-services.json.",
122
136
  error
123
137
  )
124
138
  }
125
139
 
126
- val token = awaitTokenWithRetry()
140
+ val token = awaitTokenWithRetry(context)
127
141
 
128
142
  if (token.isNullOrEmpty()) {
129
- throw IllegalStateException("Firebase returned an empty FCM token")
143
+ throw PushSignalException("E_FCM_TOKEN", "Firebase returned an empty FCM token")
130
144
  }
131
145
 
132
146
  return token
@@ -134,7 +148,8 @@ internal object PushSignalCenter : Application.ActivityLifecycleCallbacks {
134
148
 
135
149
  /**
136
150
  * Fails fast with an actionable message when Google Play services cannot serve FCM
137
- * (for example a China-only ROM without GMS).
151
+ * (for example a China-only ROM without GMS). The message names the provider the
152
+ * device should use instead, so the cause is obvious in React Native logs.
138
153
  */
139
154
  private fun ensurePlayServices(context: Context) {
140
155
  val status = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context)
@@ -142,9 +157,17 @@ internal object PushSignalCenter : Application.ActivityLifecycleCallbacks {
142
157
  return
143
158
  }
144
159
 
160
+ val code = when (status) {
161
+ ConnectionResult.SERVICE_MISSING -> "E_GMS_MISSING"
162
+ ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED -> "E_GMS_UPDATE_REQUIRED"
163
+ ConnectionResult.SERVICE_DISABLED -> "E_GMS_DISABLED"
164
+ ConnectionResult.SERVICE_INVALID -> "E_GMS_INVALID"
165
+ else -> "E_GMS_UNAVAILABLE"
166
+ }
167
+
145
168
  val reason = when (status) {
146
169
  ConnectionResult.SERVICE_MISSING ->
147
- "Google Play services are missing on this device, so FCM cannot be used. A ROM without GMS needs another push provider."
170
+ "Google Play services are missing on this device, so the standard Google push service (FCM) cannot be used."
148
171
  ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED ->
149
172
  "Google Play services are outdated. Update them in the Play Store and try again."
150
173
  ConnectionResult.SERVICE_DISABLED ->
@@ -152,10 +175,118 @@ internal object PushSignalCenter : Application.ActivityLifecycleCallbacks {
152
175
  ConnectionResult.SERVICE_INVALID ->
153
176
  "Google Play services are invalid or corrupted on this device. Reinstalling them usually helps."
154
177
  else ->
155
- "Google Play services are unavailable (code $status)."
178
+ "Google Play services are unavailable (code $status), so the standard Google push service (FCM) cannot be used."
179
+ }
180
+
181
+ val diagnostics = diagnose(context)
182
+ val message = listOfNotNull(reason, diagnostics.hint).joinToString(" ")
183
+ Log.w(TAG, "Play services check failed ($code): $message")
184
+ throw PushSignalException(code, message)
185
+ }
186
+
187
+ /**
188
+ * Collects device and provider diagnostics without throwing. Used both by the
189
+ * `getDiagnostics` API and to enrich errors with a concrete replacement service.
190
+ */
191
+ fun diagnose(context: Context): PushDiagnostics {
192
+ val status = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context)
193
+ val gmsAvailable = status == ConnectionResult.SUCCESS
194
+ val provider = resolveProvider(context, gmsAvailable)
195
+ val hint = providerHint(provider, gmsAvailable)
196
+
197
+ return PushDiagnostics(
198
+ platform = "android_os",
199
+ gmsAvailable = gmsAvailable,
200
+ gmsStatus = status,
201
+ manufacturer = Build.MANUFACTURER ?: "",
202
+ brand = Build.BRAND ?: "",
203
+ model = Build.MODEL ?: "",
204
+ provider = provider.id,
205
+ providerName = provider.name,
206
+ providerInstalled = provider.installed,
207
+ hint = hint,
208
+ )
209
+ }
210
+
211
+ private data class ProviderInfo(
212
+ val id: String,
213
+ val name: String,
214
+ val installed: Boolean?,
215
+ )
216
+
217
+ /**
218
+ * Picks the push provider this device should use. Checks the standard Google
219
+ * service first, then positively confirms an installed alternative, then falls
220
+ * back to the manufacturer mapping. Class reflection cannot see another
221
+ * provider's SDK unless the host bundles it, so package checks and the
222
+ * manufacturer are the reliable signals.
223
+ */
224
+ private fun resolveProvider(context: Context, gmsAvailable: Boolean): ProviderInfo {
225
+ if (gmsAvailable) {
226
+ return ProviderInfo(PROVIDER_FCM, "Firebase Cloud Messaging (FCM)", true)
227
+ }
228
+
229
+ if (isPackageInstalled(context, PACKAGE_HMS)) {
230
+ return ProviderInfo(PROVIDER_HMS, "HMS Push Kit (Huawei Push)", true)
231
+ }
232
+ if (isPackageInstalled(context, PACKAGE_MI_PUSH)) {
233
+ return ProviderInfo(PROVIDER_MI_PUSH, "Mi Push (Xiaomi Push)", true)
234
+ }
235
+
236
+ val keys = setOf(Build.MANUFACTURER, Build.BRAND)
237
+ .filterNotNull()
238
+ .map { it.uppercase() }
239
+
240
+ return when {
241
+ keys.any { it.contains("HUAWEI") || it.contains("HONOR") } ->
242
+ ProviderInfo(PROVIDER_HMS, "HMS Push Kit (Huawei Push)", false)
243
+ keys.any { it.contains("XIAOMI") || it.contains("REDMI") || it.contains("POCO") } ->
244
+ ProviderInfo(PROVIDER_MI_PUSH, "Mi Push (Xiaomi Push)", false)
245
+ keys.any { it.contains("OPPO") || it.contains("ONEPLUS") || it.contains("REALME") } ->
246
+ ProviderInfo(PROVIDER_OPPO_PUSH, "OPPO Push (HeyTap)", false)
247
+ keys.any { it.contains("VIVO") || it.contains("IQOO") } ->
248
+ ProviderInfo(PROVIDER_VIVO_PUSH, "vivo Push", false)
249
+ keys.any { it.contains("MEIZU") } ->
250
+ ProviderInfo(PROVIDER_MEIZU_PUSH, "Meizu Push", false)
251
+ else ->
252
+ ProviderInfo(PROVIDER_UNKNOWN, "unknown", null)
253
+ }
254
+ }
255
+
256
+ private fun providerHint(provider: ProviderInfo, gmsAvailable: Boolean): String? {
257
+ if (gmsAvailable) {
258
+ return null
259
+ }
260
+ val device = listOfNotNull(Build.MANUFACTURER, Build.BRAND)
261
+ .filter { it.isNotBlank() }
262
+ .distinct()
263
+ .joinToString("/")
264
+
265
+ return when (provider.id) {
266
+ PROVIDER_HMS ->
267
+ "This device ($device) has no usable Google Play services, so FCM will not issue a token. Use HMS Push Kit (Huawei Push) instead of the standard Google service."
268
+ PROVIDER_MI_PUSH ->
269
+ "This device ($device) has no usable Google Play services, so FCM will not issue a token. Use Mi Push (Xiaomi Push) instead of the standard Google service."
270
+ PROVIDER_OPPO_PUSH ->
271
+ "This device ($device) has no usable Google Play services, so FCM will not issue a token. Use OPPO Push (HeyTap) instead of the standard Google service."
272
+ PROVIDER_VIVO_PUSH ->
273
+ "This device ($device) has no usable Google Play services, so FCM will not issue a token. Use vivo Push instead of the standard Google service."
274
+ PROVIDER_MEIZU_PUSH ->
275
+ "This device ($device) has no usable Google Play services, so FCM will not issue a token. Use Meizu Push instead of the standard Google service."
276
+ else ->
277
+ "No usable Google Play services were found and no known push provider was detected on this ROM, so FCM cannot be used. Integrate another push provider (HMS, Mi Push, OPPO/vivo/Meizu Push)."
278
+ }
279
+ }
280
+
281
+ private fun isPackageInstalled(context: Context, packageName: String): Boolean {
282
+ return try {
283
+ context.packageManager.getPackageInfo(packageName, 0)
284
+ true
285
+ } catch (_: PackageManager.NameNotFoundException) {
286
+ false
287
+ } catch (_: Exception) {
288
+ false
156
289
  }
157
- Log.w(TAG, "Play services check failed: $reason")
158
- throw IllegalStateException(reason)
159
290
  }
160
291
 
161
292
  /**
@@ -163,7 +294,7 @@ internal object PushSignalCenter : Application.ActivityLifecycleCallbacks {
163
294
  * Play services still starting, flaky network). Xiaomi/MIUI devices hit this often,
164
295
  * so retry with backoff before surfacing the failure.
165
296
  */
166
- private fun awaitTokenWithRetry(): String? {
297
+ private fun awaitTokenWithRetry(context: Context): String? {
167
298
  var delayMs = TOKEN_INITIAL_RETRY_DELAY_MS
168
299
  var lastError: Exception? = null
169
300
 
@@ -191,13 +322,16 @@ internal object PushSignalCenter : Application.ActivityLifecycleCallbacks {
191
322
  }
192
323
 
193
324
  val cause = lastError
194
- throw IllegalStateException(
195
- "Failed to get an FCM token: ${cause?.message ?: "unknown error"}. " +
196
- "Make sure the device is signed into a Google account, Google Play services are up to date, " +
197
- "and the app is allowed to use background data. " +
198
- "Call initialize({ project_id, mobilesdk_app_id, current_key, project_number }) or add google-services.json.",
199
- cause
200
- )
325
+ val diagnostics = diagnose(context)
326
+ val message = listOfNotNull(
327
+ "Failed to get an FCM token: ${cause?.message ?: "unknown error"}.",
328
+ "Make sure the device is signed into a Google account, Google Play services are up to date, " +
329
+ "and the app is allowed to use background data.",
330
+ "Call initialize({ project_id, mobilesdk_app_id, current_key, project_number }) or add google-services.json.",
331
+ diagnostics.hint,
332
+ ).joinToString(" ")
333
+ Log.w(TAG, "Failed to get an FCM token: $message")
334
+ throw PushSignalException("E_FCM_TOKEN", message, cause)
201
335
  }
202
336
 
203
337
  private fun isRetryableTokenError(error: Throwable): Boolean {
@@ -26,7 +26,8 @@ class PushSignalModule(reactContext: ReactApplicationContext) :
26
26
  if (error == null) {
27
27
  promise.resolve(null)
28
28
  } else {
29
- promise.reject("E_INIT", error.message, error)
29
+ val code = (error as? PushSignalException)?.code ?: "E_INIT"
30
+ promise.reject(code, error.message, error)
30
31
  }
31
32
  }
32
33
  }
@@ -37,12 +38,23 @@ class PushSignalModule(reactContext: ReactApplicationContext) :
37
38
  PushSignalCenter.attach(reactApplicationContext)
38
39
  val token = PushSignalCenter.fetchToken()
39
40
  val result = Arguments.createMap().apply {
40
- putString("platform", "android_os")
41
41
  putString("token", token)
42
42
  }
43
43
  promise.resolve(result)
44
44
  } catch (error: Exception) {
45
- promise.reject("E_CREDENTIALS", error.message, error)
45
+ val code = (error as? PushSignalException)?.code ?: "E_CREDENTIALS"
46
+ promise.reject(code, error.message, error)
47
+ }
48
+ }
49
+ }
50
+
51
+ override fun getDiagnostics(promise: Promise) {
52
+ executor.execute {
53
+ try {
54
+ PushSignalCenter.attach(reactApplicationContext)
55
+ promise.resolve(PushSignalCenter.diagnose(reactApplicationContext).toWritableMap())
56
+ } catch (error: Exception) {
57
+ promise.reject("E_DIAGNOSTICS", error.message, error)
46
58
  }
47
59
  }
48
60
  }
@@ -86,3 +98,18 @@ private fun PushMessage.toWritableMap(): WritableMap {
86
98
  map.putMap("data", dataMap)
87
99
  return map
88
100
  }
101
+
102
+ private fun PushDiagnostics.toWritableMap(): WritableMap {
103
+ val map = Arguments.createMap()
104
+ map.putString("platform", platform)
105
+ map.putBoolean("gmsAvailable", gmsAvailable)
106
+ map.putInt("gmsStatus", gmsStatus)
107
+ map.putString("manufacturer", manufacturer)
108
+ map.putString("brand", brand)
109
+ map.putString("model", model)
110
+ map.putString("provider", provider)
111
+ map.putString("providerName", providerName)
112
+ providerInstalled?.let { map.putBoolean("providerInstalled", it) }
113
+ hint?.let { map.putString("hint", it) }
114
+ return map
115
+ }
package/ios/PushSignal.mm CHANGED
@@ -1,5 +1,6 @@
1
1
  #import "PushSignal.h"
2
2
  #import "PushSignalCenter.h"
3
+ #import <UIKit/UIKit.h>
3
4
 
4
5
  @implementation PushSignal {
5
6
  BOOL _listening;
@@ -22,6 +23,23 @@
22
23
  }];
23
24
  }
24
25
 
26
+ - (void)getDiagnostics:(RCTPromiseResolveBlock)resolve
27
+ reject:(RCTPromiseRejectBlock)reject {
28
+ (void)reject;
29
+ UIDevice *device = [UIDevice currentDevice];
30
+ resolve(@{
31
+ @"platform": @"ios",
32
+ @"gmsAvailable": @YES,
33
+ @"gmsStatus": @0,
34
+ @"manufacturer": @"Apple",
35
+ @"brand": @"Apple",
36
+ @"model": device.model ?: @"iOS device",
37
+ @"provider": @"apns",
38
+ @"providerName": @"Apple Push Notification service (APNs)",
39
+ @"providerInstalled": @YES,
40
+ });
41
+ }
42
+
25
43
  - (void)startListening {
26
44
  if (_listening) {
27
45
  return;
@@ -111,7 +111,6 @@
111
111
  return;
112
112
  }
113
113
  NSMutableDictionary *credentials = [NSMutableDictionary dictionary];
114
- credentials[@"platform"] = @"ios";
115
114
  credentials[@"token"] = token ?: @"";
116
115
  credentials[@"environment"] = [self currentEnvironment];
117
116
  resolve(credentials);
@@ -1 +1 @@
1
- {"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativePushSignal.ts"],"mappings":";;AACA,SAASA,mBAAmB,QAAQ,cAAc;AAuBlD,eAAeA,mBAAmB,CAACC,YAAY,CAAO,YAAY,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativePushSignal.ts"],"mappings":";;AACA,SAASA,mBAAmB,QAAQ,cAAc;AA0ClD,eAAeA,mBAAmB,CAACC,YAAY,CAAO,YAAY,CAAC","ignoreList":[]}
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Error thrown by `initialize` and `getCredentials`.
5
+ *
6
+ * The message already contains a human-readable, actionable hint when the
7
+ * standard Google service (FCM/GMS) cannot be used. Structured details are
8
+ * available on the instance so the host app can decide how to log or report them.
9
+ */
10
+ export class PushSignalError extends Error {
11
+ /** Machine-readable code, e.g. `E_GMS_MISSING` or `E_FCM_TOKEN`. */
12
+
13
+ /** Suggested alternative push provider, when the standard service is unusable. */
14
+
15
+ /** Actionable sentence: which service to use instead of the standard one. */
16
+
17
+ /** Full device/provider diagnostics if they could be collected. */
18
+
19
+ /** Original error from the native layer. */
20
+
21
+ constructor(code, message, cause) {
22
+ super(message);
23
+ this.name = 'PushSignalError';
24
+ this.code = code;
25
+ this.cause = cause;
26
+ Object.setPrototypeOf(this, PushSignalError.prototype);
27
+ }
28
+ }
29
+ //# sourceMappingURL=PushSignalError.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["PushSignalError","Error","constructor","code","message","cause","name","Object","setPrototypeOf","prototype"],"sourceRoot":"../../src","sources":["PushSignalError.ts"],"mappings":";;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMA,eAAe,SAASC,KAAK,CAAC;EACzC;;EAEA;;EAEA;;EAEA;;EAEA;;EAGAC,WAAWA,CAACC,IAAY,EAAEC,OAAe,EAAEC,KAAe,EAAE;IAC1D,KAAK,CAACD,OAAO,CAAC;IACd,IAAI,CAACE,IAAI,GAAG,iBAAiB;IAC7B,IAAI,CAACH,IAAI,GAAGA,IAAI;IAChB,IAAI,CAACE,KAAK,GAAGA,KAAK;IAClBE,MAAM,CAACC,cAAc,CAAC,IAAI,EAAER,eAAe,CAACS,SAAS,CAAC;EACxD;AACF","ignoreList":[]}
@@ -1,4 +1,5 @@
1
1
  "use strict";
2
2
 
3
- export { getCredentials, initialize, onMessage, onNotificationPress } from './pushSignal';
3
+ export { PushSignalError } from "./PushSignalError.js";
4
+ export { getCredentials, getDiagnostics, initialize, onMessage, onNotificationPress } from './pushSignal';
4
5
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["getCredentials","initialize","onMessage","onNotificationPress"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAQA,SACEA,cAAc,EACdC,UAAU,EACVC,SAAS,EACTC,mBAAmB,QACd,cAAc","ignoreList":[]}
1
+ {"version":3,"names":["PushSignalError","getCredentials","getDiagnostics","initialize","onMessage","onNotificationPress"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAUA,SAASA,eAAe,QAAQ,sBAAmB;AACnD,SACEC,cAAc,EACdC,cAAc,EACdC,UAAU,EACVC,SAAS,EACTC,mBAAmB,QACd,cAAc","ignoreList":[]}
@@ -4,6 +4,9 @@ export async function initialize(_config = {}) {}
4
4
  export async function getCredentials() {
5
5
  throw new Error('Push credentials are not supported on web');
6
6
  }
7
+ export async function getDiagnostics() {
8
+ throw new Error('Push diagnostics are not supported on web');
9
+ }
7
10
  export function onMessage(_listener) {
8
11
  return () => {};
9
12
  }
@@ -1 +1 @@
1
- {"version":3,"names":["initialize","_config","getCredentials","Error","onMessage","_listener","onNotificationPress"],"sourceRoot":"../../src","sources":["pushSignal.tsx"],"mappings":";;AAOA,OAAO,eAAeA,UAAUA,CAC9BC,OAA8B,GAAG,CAAC,CAAC,EACpB,CAAC;AAElB,OAAO,eAAeC,cAAcA,CAAA,EAA6B;EAC/D,MAAM,IAAIC,KAAK,CAAC,2CAA2C,CAAC;AAC9D;AAEA,OAAO,SAASC,SAASA,CAACC,SAA4B,EAAc;EAClE,OAAO,MAAM,CAAC,CAAC;AACjB;AAEA,OAAO,SAASC,mBAAmBA,CACjCD,SAAyC,EAC7B;EACZ,OAAO,MAAM,CAAC,CAAC;AACjB","ignoreList":[]}
1
+ {"version":3,"names":["initialize","_config","getCredentials","Error","getDiagnostics","onMessage","_listener","onNotificationPress"],"sourceRoot":"../../src","sources":["pushSignal.tsx"],"mappings":";;AAQA,OAAO,eAAeA,UAAUA,CAC9BC,OAA8B,GAAG,CAAC,CAAC,EACpB,CAAC;AAElB,OAAO,eAAeC,cAAcA,CAAA,EAA6B;EAC/D,MAAM,IAAIC,KAAK,CAAC,2CAA2C,CAAC;AAC9D;AAEA,OAAO,eAAeC,cAAcA,CAAA,EAA6B;EAC/D,MAAM,IAAID,KAAK,CAAC,2CAA2C,CAAC;AAC9D;AAEA,OAAO,SAASE,SAASA,CAACC,SAA4B,EAAc;EAClE,OAAO,MAAM,CAAC,CAAC;AACjB;AAEA,OAAO,SAASC,mBAAmBA,CACjCD,SAAyC,EAC7B;EACZ,OAAO,MAAM,CAAC,CAAC;AACjB","ignoreList":[]}
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
 
3
3
  import NativePushSignal from "./NativePushSignal.js";
4
+ import { PushSignalError } from "./PushSignalError.js";
4
5
  const messageListeners = new Set();
5
6
  const pressListeners = new Set();
6
7
  let nativeCallbacksBound = false;
@@ -23,11 +24,44 @@ function normalizeMessage(raw) {
23
24
  }
24
25
  function normalizeCredentials(raw) {
25
26
  return {
26
- platform: raw.platform,
27
27
  token: raw.token,
28
28
  environment: raw.environment
29
29
  };
30
30
  }
31
+ function normalizeDiagnostics(raw) {
32
+ return {
33
+ platform: raw.platform,
34
+ gmsAvailable: raw.gmsAvailable,
35
+ gmsStatus: raw.gmsStatus,
36
+ manufacturer: raw.manufacturer,
37
+ brand: raw.brand,
38
+ model: raw.model,
39
+ provider: raw.provider,
40
+ providerName: raw.providerName,
41
+ providerInstalled: raw.providerInstalled ?? undefined,
42
+ hint: raw.hint ?? undefined
43
+ };
44
+ }
45
+ function toPushSignalError(error, fallbackCode) {
46
+ const candidate = error;
47
+ return new PushSignalError(candidate?.code ?? fallbackCode, candidate?.message ?? 'PushSignal request failed', error);
48
+ }
49
+
50
+ /**
51
+ * Attaches structured diagnostics to an error on a best-effort basis. Failing to
52
+ * collect them must never hide the original, already actionable error.
53
+ */
54
+ async function enrichError(error) {
55
+ try {
56
+ const diagnostics = normalizeDiagnostics(await NativePushSignal.getDiagnostics());
57
+ error.diagnostics = diagnostics;
58
+ error.provider = diagnostics.provider;
59
+ error.hint = diagnostics.hint;
60
+ } catch {
61
+ // Diagnostics are optional; keep the original error.
62
+ }
63
+ return error;
64
+ }
31
65
  function bindNativeCallbacks() {
32
66
  if (nativeCallbacksBound) {
33
67
  return;
@@ -49,11 +83,23 @@ function bindNativeCallbacks() {
49
83
  });
50
84
  NativePushSignal.startListening();
51
85
  }
52
- export function initialize(config = {}) {
53
- return NativePushSignal.initialize(config);
86
+ export async function initialize(config = {}) {
87
+ try {
88
+ await NativePushSignal.initialize(config);
89
+ } catch (error) {
90
+ throw await enrichError(toPushSignalError(error, 'E_INIT'));
91
+ }
92
+ }
93
+ export async function getCredentials() {
94
+ try {
95
+ const raw = await NativePushSignal.getCredentials();
96
+ return normalizeCredentials(raw);
97
+ } catch (error) {
98
+ throw await enrichError(toPushSignalError(error, 'E_CREDENTIALS'));
99
+ }
54
100
  }
55
- export function getCredentials() {
56
- return NativePushSignal.getCredentials().then(normalizeCredentials);
101
+ export async function getDiagnostics() {
102
+ return normalizeDiagnostics(await NativePushSignal.getDiagnostics());
57
103
  }
58
104
  export function onMessage(listener) {
59
105
  bindNativeCallbacks();
@@ -1 +1 @@
1
- {"version":3,"names":["NativePushSignal","messageListeners","Set","pressListeners","nativeCallbacksBound","normalizeMessage","raw","data","key","value","Object","entries","String","id","title","body","normalizeCredentials","platform","token","environment","bindNativeCallbacks","onMessage","message","listener","Promise","resolve","then","undefined","onNotificationPress","forEach","startListening","initialize","config","getCredentials","add","delete"],"sourceRoot":"../../src","sources":["pushSignal.native.tsx"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["NativePushSignal","PushSignalError","messageListeners","Set","pressListeners","nativeCallbacksBound","normalizeMessage","raw","data","key","value","Object","entries","String","id","title","body","normalizeCredentials","token","environment","normalizeDiagnostics","platform","gmsAvailable","gmsStatus","manufacturer","brand","model","provider","providerName","providerInstalled","undefined","hint","toPushSignalError","error","fallbackCode","candidate","code","message","enrichError","diagnostics","getDiagnostics","bindNativeCallbacks","onMessage","listener","Promise","resolve","then","onNotificationPress","forEach","startListening","initialize","config","getCredentials","add","delete"],"sourceRoot":"../../src","sources":["pushSignal.native.tsx"],"mappings":"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","ignoreList":[]}
@@ -6,13 +6,31 @@ export type NativePushMessage = {
6
6
  data: Object;
7
7
  };
8
8
  export type NativePushCredentials = {
9
- platform: string;
10
9
  token: string;
11
10
  environment?: string;
12
11
  };
12
+ export type NativePushDiagnostics = {
13
+ platform: string;
14
+ /** True when Google Play services can serve FCM; false on a ROM without GMS. */
15
+ gmsAvailable: boolean;
16
+ /** Raw GoogleApiAvailability status code. 0 means SUCCESS. */
17
+ gmsStatus: number;
18
+ manufacturer: string;
19
+ brand: string;
20
+ model: string;
21
+ /** Machine-readable provider id: fcm, hms, mi_push, oppo_push, vivo_push, meizu_push, apns, unknown. */
22
+ provider: string;
23
+ /** Human-readable provider name, e.g. "HMS Push Kit". */
24
+ providerName: string;
25
+ /** True when the provider service app was found on the device. */
26
+ providerInstalled: boolean;
27
+ /** Actionable sentence explaining which service to use instead of the standard one. */
28
+ hint?: string;
29
+ };
13
30
  export interface Spec extends TurboModule {
14
31
  initialize(config: Object): Promise<void>;
15
32
  getCredentials(): Promise<NativePushCredentials>;
33
+ getDiagnostics(): Promise<NativePushDiagnostics>;
16
34
  startListening(): void;
17
35
  readonly onMessage: CodegenTypes.EventEmitter<NativePushMessage>;
18
36
  readonly onNotificationPress: CodegenTypes.EventEmitter<NativePushMessage>;
@@ -1 +1 @@
1
- {"version":3,"file":"NativePushSignal.d.ts","sourceRoot":"","sources":["../../../src/NativePushSignal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAG9D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,cAAc,IAAI,OAAO,CAAC,qBAAqB,CAAC,CAAC;IACjD,cAAc,IAAI,IAAI,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC,YAAY,CAAC,iBAAiB,CAAC,CAAC;IACjE,QAAQ,CAAC,mBAAmB,EAAE,YAAY,CAAC,YAAY,CAAC,iBAAiB,CAAC,CAAC;CAC5E;;AAED,wBAAoE"}
1
+ {"version":3,"file":"NativePushSignal.d.ts","sourceRoot":"","sources":["../../../src/NativePushSignal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAG9D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,gFAAgF;IAChF,YAAY,EAAE,OAAO,CAAC;IACtB,8DAA8D;IAC9D,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,wGAAwG;IACxG,QAAQ,EAAE,MAAM,CAAC;IACjB,yDAAyD;IACzD,YAAY,EAAE,MAAM,CAAC;IACrB,kEAAkE;IAClE,iBAAiB,EAAE,OAAO,CAAC;IAC3B,uFAAuF;IACvF,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,cAAc,IAAI,OAAO,CAAC,qBAAqB,CAAC,CAAC;IACjD,cAAc,IAAI,OAAO,CAAC,qBAAqB,CAAC,CAAC;IACjD,cAAc,IAAI,IAAI,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC,YAAY,CAAC,iBAAiB,CAAC,CAAC;IACjE,QAAQ,CAAC,mBAAmB,EAAE,YAAY,CAAC,YAAY,CAAC,iBAAiB,CAAC,CAAC;CAC5E;;AAED,wBAAoE"}
@@ -0,0 +1,22 @@
1
+ import type { PushDiagnostics, PushProvider } from './types.js';
2
+ /**
3
+ * Error thrown by `initialize` and `getCredentials`.
4
+ *
5
+ * The message already contains a human-readable, actionable hint when the
6
+ * standard Google service (FCM/GMS) cannot be used. Structured details are
7
+ * available on the instance so the host app can decide how to log or report them.
8
+ */
9
+ export declare class PushSignalError extends Error {
10
+ /** Machine-readable code, e.g. `E_GMS_MISSING` or `E_FCM_TOKEN`. */
11
+ readonly code: string;
12
+ /** Suggested alternative push provider, when the standard service is unusable. */
13
+ provider?: PushProvider;
14
+ /** Actionable sentence: which service to use instead of the standard one. */
15
+ hint?: string;
16
+ /** Full device/provider diagnostics if they could be collected. */
17
+ diagnostics?: PushDiagnostics;
18
+ /** Original error from the native layer. */
19
+ readonly cause?: unknown;
20
+ constructor(code: string, message: string, cause?: unknown);
21
+ }
22
+ //# sourceMappingURL=PushSignalError.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PushSignalError.d.ts","sourceRoot":"","sources":["../../../src/PushSignalError.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,YAAS,CAAC;AAE7D;;;;;;GAMG;AACH,qBAAa,eAAgB,SAAQ,KAAK;IACxC,oEAAoE;IACpE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,kFAAkF;IAClF,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,6EAA6E;IAC7E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,4CAA4C;IAC5C,SAAkB,KAAK,CAAC,EAAE,OAAO,CAAC;gBAEtB,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO;CAO3D"}
@@ -1,3 +1,4 @@
1
- export type { AndroidFirebaseConfig, OnMessageListener, PushCredentials, PushEnvironment, PushMessage, PushPlatform, } from './types.js';
2
- export { getCredentials, initialize, onMessage, onNotificationPress, } from './pushSignal';
1
+ export type { AndroidFirebaseConfig, OnMessageListener, PushCredentials, PushDiagnostics, PushEnvironment, PushMessage, PushPlatform, PushProvider, } from './types.js';
2
+ export { PushSignalError } from './PushSignalError.js';
3
+ export { getCredentials, getDiagnostics, initialize, onMessage, onNotificationPress, } from './pushSignal';
3
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,YAAY,EACV,qBAAqB,EACrB,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,WAAW,EACX,YAAY,GACb,MAAM,YAAS,CAAC;AACjB,OAAO,EACL,cAAc,EACd,UAAU,EACV,SAAS,EACT,mBAAmB,GACpB,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,YAAY,EACV,qBAAqB,EACrB,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,eAAe,EACf,WAAW,EACX,YAAY,EACZ,YAAY,GACb,MAAM,YAAS,CAAC;AACjB,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAmB,CAAC;AACpD,OAAO,EACL,cAAc,EACd,cAAc,EACd,UAAU,EACV,SAAS,EACT,mBAAmB,GACpB,MAAM,cAAc,CAAC"}
@@ -1,6 +1,7 @@
1
- import type { AndroidFirebaseConfig, OnMessageListener, PushCredentials, PushMessage } from './types.js';
1
+ import type { AndroidFirebaseConfig, OnMessageListener, PushCredentials, PushDiagnostics, PushMessage } from './types.js';
2
2
  export declare function initialize(_config?: AndroidFirebaseConfig): Promise<void>;
3
3
  export declare function getCredentials(): Promise<PushCredentials>;
4
+ export declare function getDiagnostics(): Promise<PushDiagnostics>;
4
5
  export declare function onMessage(_listener: OnMessageListener): () => void;
5
6
  export declare function onNotificationPress(_listener: (message: PushMessage) => void): () => void;
6
7
  //# sourceMappingURL=pushSignal.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pushSignal.d.ts","sourceRoot":"","sources":["../../../src/pushSignal.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,iBAAiB,EACjB,eAAe,EACf,WAAW,EACZ,MAAM,YAAS,CAAC;AAEjB,wBAAsB,UAAU,CAC9B,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,IAAI,CAAC,CAAG;AAEnB,wBAAsB,cAAc,IAAI,OAAO,CAAC,eAAe,CAAC,CAE/D;AAED,wBAAgB,SAAS,CAAC,SAAS,EAAE,iBAAiB,GAAG,MAAM,IAAI,CAElE;AAED,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,GACxC,MAAM,IAAI,CAEZ"}
1
+ {"version":3,"file":"pushSignal.d.ts","sourceRoot":"","sources":["../../../src/pushSignal.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,WAAW,EACZ,MAAM,YAAS,CAAC;AAEjB,wBAAsB,UAAU,CAC9B,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,IAAI,CAAC,CAAG;AAEnB,wBAAsB,cAAc,IAAI,OAAO,CAAC,eAAe,CAAC,CAE/D;AAED,wBAAsB,cAAc,IAAI,OAAO,CAAC,eAAe,CAAC,CAE/D;AAED,wBAAgB,SAAS,CAAC,SAAS,EAAE,iBAAiB,GAAG,MAAM,IAAI,CAElE;AAED,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,GACxC,MAAM,IAAI,CAEZ"}
@@ -1,6 +1,7 @@
1
- import type { AndroidFirebaseConfig, OnMessageListener, PushCredentials, PushMessage } from './types.js';
1
+ import type { AndroidFirebaseConfig, OnMessageListener, PushCredentials, PushDiagnostics, PushMessage } from './types.js';
2
2
  export declare function initialize(config?: AndroidFirebaseConfig): Promise<void>;
3
3
  export declare function getCredentials(): Promise<PushCredentials>;
4
+ export declare function getDiagnostics(): Promise<PushDiagnostics>;
4
5
  export declare function onMessage(listener: OnMessageListener): () => void;
5
6
  export declare function onNotificationPress(listener: (message: PushMessage) => void): () => void;
6
7
  //# sourceMappingURL=pushSignal.native.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pushSignal.native.d.ts","sourceRoot":"","sources":["../../../src/pushSignal.native.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,qBAAqB,EACrB,iBAAiB,EACjB,eAAe,EAEf,WAAW,EAEZ,MAAM,YAAS,CAAC;AAyEjB,wBAAgB,UAAU,CAAC,MAAM,GAAE,qBAA0B,GAAG,OAAO,CAAC,IAAI,CAAC,CAE5E;AAED,wBAAgB,cAAc,IAAI,OAAO,CAAC,eAAe,CAAC,CAEzD;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,iBAAiB,GAAG,MAAM,IAAI,CAMjE;AAED,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,GACvC,MAAM,IAAI,CAMZ"}
1
+ {"version":3,"file":"pushSignal.native.d.ts","sourceRoot":"","sources":["../../../src/pushSignal.native.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,qBAAqB,EACrB,iBAAiB,EACjB,eAAe,EACf,eAAe,EAEf,WAAW,EAGZ,MAAM,YAAS,CAAC;AAoHjB,wBAAsB,UAAU,CAC9B,MAAM,GAAE,qBAA0B,GACjC,OAAO,CAAC,IAAI,CAAC,CAMf;AAED,wBAAsB,cAAc,IAAI,OAAO,CAAC,eAAe,CAAC,CAO/D;AAED,wBAAsB,cAAc,IAAI,OAAO,CAAC,eAAe,CAAC,CAE/D;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,iBAAiB,GAAG,MAAM,IAAI,CAMjE;AAED,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,GACvC,MAAM,IAAI,CAMZ"}
@@ -2,7 +2,6 @@
2
2
  export type PushPlatform = 'ios' | 'android_os';
3
3
  export type PushEnvironment = 'sandbox' | 'production';
4
4
  export interface PushCredentials {
5
- platform: PushPlatform;
6
5
  token: string;
7
6
  environment?: PushEnvironment;
8
7
  }
@@ -13,6 +12,24 @@ export interface PushMessage {
13
12
  data: Record<string, string>;
14
13
  }
15
14
  export type OnMessageListener = (message: PushMessage) => void | Promise<void>;
15
+ export type PushProvider = 'fcm' | 'hms' | 'mi_push' | 'oppo_push' | 'vivo_push' | 'meizu_push' | 'apns' | 'unknown';
16
+ export interface PushDiagnostics {
17
+ platform: PushPlatform;
18
+ /** True when Google Play services can serve FCM; false on a ROM without GMS. */
19
+ gmsAvailable: boolean;
20
+ /** Raw GoogleApiAvailability status code. 0 means SUCCESS. */
21
+ gmsStatus: number;
22
+ manufacturer: string;
23
+ brand: string;
24
+ model: string;
25
+ provider: PushProvider;
26
+ /** Human-readable provider name, e.g. "HMS Push Kit". */
27
+ providerName: string;
28
+ /** True only when the provider service app was positively found on the device. */
29
+ providerInstalled?: boolean;
30
+ /** Actionable sentence explaining which service to use instead of the standard one. */
31
+ hint?: string;
32
+ }
16
33
  export interface AndroidFirebaseConfig {
17
34
  project_id?: string;
18
35
  mobilesdk_app_id?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,YAAY,CAAC;AAChD,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,YAAY,CAAC;AAEvD,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,YAAY,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,eAAe,CAAC;CAC/B;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAED,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE/E,MAAM,WAAW,qBAAqB;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,YAAY,CAAC;AAChD,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,YAAY,CAAC;AAEvD,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,eAAe,CAAC;CAC/B;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAED,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE/E,MAAM,MAAM,YAAY,GACpB,KAAK,GACL,KAAK,GACL,SAAS,GACT,WAAW,GACX,WAAW,GACX,YAAY,GACZ,MAAM,GACN,SAAS,CAAC;AAEd,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,YAAY,CAAC;IACvB,gFAAgF;IAChF,YAAY,EAAE,OAAO,CAAC;IACtB,8DAA8D;IAC9D,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,YAAY,CAAC;IACvB,yDAAyD;IACzD,YAAY,EAAE,MAAM,CAAC;IACrB,kFAAkF;IAClF,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,uFAAuF;IACvF,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,qBAAqB;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-push-signal",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "React Native push notifications with direct APNs and FCM integration — get device tokens for your server and listen for incoming messages and taps",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -9,14 +9,33 @@ export type NativePushMessage = {
9
9
  };
10
10
 
11
11
  export type NativePushCredentials = {
12
- platform: string;
13
12
  token: string;
14
13
  environment?: string;
15
14
  };
16
15
 
16
+ export type NativePushDiagnostics = {
17
+ platform: string;
18
+ /** True when Google Play services can serve FCM; false on a ROM without GMS. */
19
+ gmsAvailable: boolean;
20
+ /** Raw GoogleApiAvailability status code. 0 means SUCCESS. */
21
+ gmsStatus: number;
22
+ manufacturer: string;
23
+ brand: string;
24
+ model: string;
25
+ /** Machine-readable provider id: fcm, hms, mi_push, oppo_push, vivo_push, meizu_push, apns, unknown. */
26
+ provider: string;
27
+ /** Human-readable provider name, e.g. "HMS Push Kit". */
28
+ providerName: string;
29
+ /** True when the provider service app was found on the device. */
30
+ providerInstalled: boolean;
31
+ /** Actionable sentence explaining which service to use instead of the standard one. */
32
+ hint?: string;
33
+ };
34
+
17
35
  export interface Spec extends TurboModule {
18
36
  initialize(config: Object): Promise<void>;
19
37
  getCredentials(): Promise<NativePushCredentials>;
38
+ getDiagnostics(): Promise<NativePushDiagnostics>;
20
39
  startListening(): void;
21
40
  readonly onMessage: CodegenTypes.EventEmitter<NativePushMessage>;
22
41
  readonly onNotificationPress: CodegenTypes.EventEmitter<NativePushMessage>;
@@ -0,0 +1,29 @@
1
+ import type { PushDiagnostics, PushProvider } from './types';
2
+
3
+ /**
4
+ * Error thrown by `initialize` and `getCredentials`.
5
+ *
6
+ * The message already contains a human-readable, actionable hint when the
7
+ * standard Google service (FCM/GMS) cannot be used. Structured details are
8
+ * available on the instance so the host app can decide how to log or report them.
9
+ */
10
+ export class PushSignalError extends Error {
11
+ /** Machine-readable code, e.g. `E_GMS_MISSING` or `E_FCM_TOKEN`. */
12
+ readonly code: string;
13
+ /** Suggested alternative push provider, when the standard service is unusable. */
14
+ provider?: PushProvider;
15
+ /** Actionable sentence: which service to use instead of the standard one. */
16
+ hint?: string;
17
+ /** Full device/provider diagnostics if they could be collected. */
18
+ diagnostics?: PushDiagnostics;
19
+ /** Original error from the native layer. */
20
+ override readonly cause?: unknown;
21
+
22
+ constructor(code: string, message: string, cause?: unknown) {
23
+ super(message);
24
+ this.name = 'PushSignalError';
25
+ this.code = code;
26
+ this.cause = cause;
27
+ Object.setPrototypeOf(this, PushSignalError.prototype);
28
+ }
29
+ }
package/src/index.tsx CHANGED
@@ -2,12 +2,16 @@ export type {
2
2
  AndroidFirebaseConfig,
3
3
  OnMessageListener,
4
4
  PushCredentials,
5
+ PushDiagnostics,
5
6
  PushEnvironment,
6
7
  PushMessage,
7
8
  PushPlatform,
9
+ PushProvider,
8
10
  } from './types';
11
+ export { PushSignalError } from './PushSignalError';
9
12
  export {
10
13
  getCredentials,
14
+ getDiagnostics,
11
15
  initialize,
12
16
  onMessage,
13
17
  onNotificationPress,
@@ -1,11 +1,15 @@
1
1
  import NativePushSignal from './NativePushSignal';
2
+ import type { NativePushDiagnostics } from './NativePushSignal';
3
+ import { PushSignalError } from './PushSignalError';
2
4
  import type {
3
5
  AndroidFirebaseConfig,
4
6
  OnMessageListener,
5
7
  PushCredentials,
8
+ PushDiagnostics,
6
9
  PushEnvironment,
7
10
  PushMessage,
8
11
  PushPlatform,
12
+ PushProvider,
9
13
  } from './types';
10
14
 
11
15
  const messageListeners = new Set<OnMessageListener>();
@@ -39,17 +43,60 @@ function normalizeMessage(raw: {
39
43
  }
40
44
 
41
45
  function normalizeCredentials(raw: {
42
- platform: string;
43
46
  token: string;
44
47
  environment?: string;
45
48
  }): PushCredentials {
46
49
  return {
47
- platform: raw.platform as PushPlatform,
48
50
  token: raw.token,
49
51
  environment: raw.environment as PushEnvironment | undefined,
50
52
  };
51
53
  }
52
54
 
55
+ function normalizeDiagnostics(raw: NativePushDiagnostics): PushDiagnostics {
56
+ return {
57
+ platform: raw.platform as PushPlatform,
58
+ gmsAvailable: raw.gmsAvailable,
59
+ gmsStatus: raw.gmsStatus,
60
+ manufacturer: raw.manufacturer,
61
+ brand: raw.brand,
62
+ model: raw.model,
63
+ provider: raw.provider as PushProvider,
64
+ providerName: raw.providerName,
65
+ providerInstalled: raw.providerInstalled ?? undefined,
66
+ hint: raw.hint ?? undefined,
67
+ };
68
+ }
69
+
70
+ function toPushSignalError(
71
+ error: unknown,
72
+ fallbackCode: string
73
+ ): PushSignalError {
74
+ const candidate = error as { code?: string; message?: string } | undefined;
75
+ return new PushSignalError(
76
+ candidate?.code ?? fallbackCode,
77
+ candidate?.message ?? 'PushSignal request failed',
78
+ error
79
+ );
80
+ }
81
+
82
+ /**
83
+ * Attaches structured diagnostics to an error on a best-effort basis. Failing to
84
+ * collect them must never hide the original, already actionable error.
85
+ */
86
+ async function enrichError(error: PushSignalError): Promise<PushSignalError> {
87
+ try {
88
+ const diagnostics = normalizeDiagnostics(
89
+ await NativePushSignal.getDiagnostics()
90
+ );
91
+ error.diagnostics = diagnostics;
92
+ error.provider = diagnostics.provider;
93
+ error.hint = diagnostics.hint;
94
+ } catch {
95
+ // Diagnostics are optional; keep the original error.
96
+ }
97
+ return error;
98
+ }
99
+
53
100
  function bindNativeCallbacks() {
54
101
  if (nativeCallbacksBound) {
55
102
  return;
@@ -79,12 +126,27 @@ function bindNativeCallbacks() {
79
126
  NativePushSignal.startListening();
80
127
  }
81
128
 
82
- export function initialize(config: AndroidFirebaseConfig = {}): Promise<void> {
83
- return NativePushSignal.initialize(config);
129
+ export async function initialize(
130
+ config: AndroidFirebaseConfig = {}
131
+ ): Promise<void> {
132
+ try {
133
+ await NativePushSignal.initialize(config);
134
+ } catch (error) {
135
+ throw await enrichError(toPushSignalError(error, 'E_INIT'));
136
+ }
137
+ }
138
+
139
+ export async function getCredentials(): Promise<PushCredentials> {
140
+ try {
141
+ const raw = await NativePushSignal.getCredentials();
142
+ return normalizeCredentials(raw);
143
+ } catch (error) {
144
+ throw await enrichError(toPushSignalError(error, 'E_CREDENTIALS'));
145
+ }
84
146
  }
85
147
 
86
- export function getCredentials(): Promise<PushCredentials> {
87
- return NativePushSignal.getCredentials().then(normalizeCredentials);
148
+ export async function getDiagnostics(): Promise<PushDiagnostics> {
149
+ return normalizeDiagnostics(await NativePushSignal.getDiagnostics());
88
150
  }
89
151
 
90
152
  export function onMessage(listener: OnMessageListener): () => void {
@@ -2,6 +2,7 @@ import type {
2
2
  AndroidFirebaseConfig,
3
3
  OnMessageListener,
4
4
  PushCredentials,
5
+ PushDiagnostics,
5
6
  PushMessage,
6
7
  } from './types';
7
8
 
@@ -13,6 +14,10 @@ export async function getCredentials(): Promise<PushCredentials> {
13
14
  throw new Error('Push credentials are not supported on web');
14
15
  }
15
16
 
17
+ export async function getDiagnostics(): Promise<PushDiagnostics> {
18
+ throw new Error('Push diagnostics are not supported on web');
19
+ }
20
+
16
21
  export function onMessage(_listener: OnMessageListener): () => void {
17
22
  return () => {};
18
23
  }
package/src/types.ts CHANGED
@@ -3,7 +3,6 @@ export type PushPlatform = 'ios' | 'android_os';
3
3
  export type PushEnvironment = 'sandbox' | 'production';
4
4
 
5
5
  export interface PushCredentials {
6
- platform: PushPlatform;
7
6
  token: string;
8
7
  environment?: PushEnvironment;
9
8
  }
@@ -17,6 +16,34 @@ export interface PushMessage {
17
16
 
18
17
  export type OnMessageListener = (message: PushMessage) => void | Promise<void>;
19
18
 
19
+ export type PushProvider =
20
+ | 'fcm'
21
+ | 'hms'
22
+ | 'mi_push'
23
+ | 'oppo_push'
24
+ | 'vivo_push'
25
+ | 'meizu_push'
26
+ | 'apns'
27
+ | 'unknown';
28
+
29
+ export interface PushDiagnostics {
30
+ platform: PushPlatform;
31
+ /** True when Google Play services can serve FCM; false on a ROM without GMS. */
32
+ gmsAvailable: boolean;
33
+ /** Raw GoogleApiAvailability status code. 0 means SUCCESS. */
34
+ gmsStatus: number;
35
+ manufacturer: string;
36
+ brand: string;
37
+ model: string;
38
+ provider: PushProvider;
39
+ /** Human-readable provider name, e.g. "HMS Push Kit". */
40
+ providerName: string;
41
+ /** True only when the provider service app was positively found on the device. */
42
+ providerInstalled?: boolean;
43
+ /** Actionable sentence explaining which service to use instead of the standard one. */
44
+ hint?: string;
45
+ }
46
+
20
47
  export interface AndroidFirebaseConfig {
21
48
  project_id?: string;
22
49
  mobilesdk_app_id?: string;