@bubblsdk/react-native-sdk 3.1.6 → 4.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.
@@ -1,6 +1,6 @@
1
1
  Pod::Spec.new do |s|
2
2
  s.name = 'BubblReactNativeSdk'
3
- s.version = '3.1.6'
3
+ s.version = '4.0.0'
4
4
  s.summary = 'React Native wrapper for Bubbl SDK v3 native Android and iOS cores.'
5
5
  s.homepage = 'https://bubbl.tech'
6
6
  s.license = { :type => 'Commercial', :text => 'Copyright Bubbl. All rights reserved.' }
@@ -13,5 +13,5 @@ Pod::Spec.new do |s|
13
13
  s.static_framework = true
14
14
 
15
15
  s.dependency 'React-Core'
16
- s.dependency 'BubblSDK', '3.1.6'
16
+ s.dependency 'BubblSDK', '4.0.0'
17
17
  end
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## 4.0.0
4
+
5
+ - Primes iOS native background region monitoring when location tracking starts and after geofence/location refresh calls, so background geofence notifications can fire without relying on foreground JavaScript execution.
6
+ - Adds an iOS launch-options helper so React Native apps can restore persisted SDK state and resume native geofence monitoring before the bridge or JavaScript UI mounts.
7
+ - Aligns the React Native wrapper with the native SDK 4.0.0 release.
8
+
9
+ ## 3.1.7
10
+
11
+ - Aligns the React Native wrapper with the native SDK 3.1.7 release.
12
+ - Android and iOS default notification modals now embed YouTube media in-app instead of handing off externally.
13
+
3
14
  ## 3.1.6
4
15
 
5
16
  - Aligns the React Native wrapper with the native SDK 3.1.6 release.
package/README.md CHANGED
@@ -35,6 +35,21 @@ bundled foreground location service and WorkManager restore/refresh path, and
35
35
  iOS uses the bundled CoreLocation monitor. The host app still owns platform
36
36
  declarations and runtime permission UX.
37
37
 
38
+ For iOS background geofence cold starts, forward launch options from your
39
+ `AppDelegate` before the React Native bridge or JavaScript UI is mounted:
40
+
41
+ ```swift
42
+ import BubblReactNativeSdk
43
+
44
+ func application(
45
+ _ application: UIApplication,
46
+ didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
47
+ ) -> Bool {
48
+ BubblSdkLocationLaunchHandler.handleLaunchOptions(launchOptions as NSDictionary?)
49
+ return true
50
+ }
51
+ ```
52
+
38
53
  For apps that render their own in-app notification modal while still letting
39
54
  the native SDK trigger device notifications:
40
55
 
@@ -4,7 +4,7 @@ plugins {
4
4
  }
5
5
 
6
6
  group = "tech.bubbl.reactnative"
7
- version = "3.1.6"
7
+ version = "4.0.0"
8
8
 
9
9
  def isNewArchitectureEnabled() {
10
10
  project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
@@ -41,6 +41,6 @@ android {
41
41
 
42
42
  dependencies {
43
43
  implementation "com.facebook.react:react-android:${reactNativeVersion}"
44
- implementation "tech.bubbl.sdk:bubbl-sdk:3.1.6"
44
+ implementation "tech.bubbl.sdk:bubbl-sdk:4.0.0"
45
45
  implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1"
46
46
  }
@@ -35,6 +35,8 @@ import tech.bubbl.sdk.BubblLogLevel
35
35
  import tech.bubbl.sdk.BubblNotificationCta
36
36
  import tech.bubbl.sdk.BubblNotificationDisplayResult
37
37
  import tech.bubbl.sdk.BubblNotificationMedia
38
+ import tech.bubbl.sdk.BubblNotificationModalStyle
39
+ import tech.bubbl.sdk.BubblNotificationModalTheme
38
40
  import tech.bubbl.sdk.BubblNotificationPayload
39
41
  import tech.bubbl.sdk.BubblNotificationRenderingMode
40
42
  import tech.bubbl.sdk.BubblNotificationSource
@@ -172,6 +174,12 @@ class BubblSdkModule(
172
174
  null
173
175
  }
174
176
 
177
+ @ReactMethod
178
+ fun setDefaultNotificationModalStyle(style: ReadableMap?, promise: Promise) = resolve(promise) {
179
+ BubblSdk.setDefaultNotificationModalStyle(style?.toNotificationModalStyle())
180
+ null
181
+ }
182
+
175
183
  @ReactMethod
176
184
  fun registerPushToken(token: String, promise: Promise) = resolve(promise) {
177
185
  BubblSdk.registerPushToken(token)
@@ -288,12 +296,40 @@ class BubblSdkModule(
288
296
  }
289
297
  },
290
298
  enableDefaultNotificationModal = bool("enableDefaultNotificationModal", true),
299
+ defaultNotificationModalStyle = map("defaultNotificationModalStyle")?.toNotificationModalStyle(),
291
300
  enableDefaultSurveyUi = bool("enableDefaultSurveyUi", true),
292
301
  logLevel = enumValue(string("logLevel"), BubblLogLevel.Warn) {
293
302
  BubblLogLevel.valueOf(it.replaceFirstChar(Char::uppercaseChar))
294
303
  }
295
304
  )
296
305
 
306
+ private fun ReadableMap.toNotificationModalStyle(): BubblNotificationModalStyle =
307
+ BubblNotificationModalStyle(
308
+ theme = enumValue(string("theme"), BubblNotificationModalTheme.Light) {
309
+ if (it == "dark") BubblNotificationModalTheme.Dark else BubblNotificationModalTheme.Light
310
+ },
311
+ transparentBackdrop = bool("transparentBackdrop", true),
312
+ backdropColor = string("backdropColor"),
313
+ cardBackgroundColor = string("cardBackgroundColor"),
314
+ cardBorderColor = string("cardBorderColor"),
315
+ titleColor = string("titleColor"),
316
+ bodyColor = string("bodyColor"),
317
+ accentColor = string("accentColor"),
318
+ iconBackgroundColor = string("iconBackgroundColor"),
319
+ iconTextColor = string("iconTextColor"),
320
+ primaryButtonBackgroundColor = string("primaryButtonBackgroundColor"),
321
+ primaryButtonTextColor = string("primaryButtonTextColor"),
322
+ secondaryButtonBackgroundColor = string("secondaryButtonBackgroundColor"),
323
+ secondaryButtonTextColor = string("secondaryButtonTextColor"),
324
+ textButtonColor = string("textButtonColor"),
325
+ surveyBackgroundColor = string("surveyBackgroundColor"),
326
+ inputBackgroundColor = string("inputBackgroundColor"),
327
+ inputTextColor = string("inputTextColor"),
328
+ inputBorderColor = string("inputBorderColor"),
329
+ cornerRadius = nullableDouble("cornerRadius"),
330
+ buttonCornerRadius = nullableDouble("buttonCornerRadius")
331
+ )
332
+
297
333
  private fun ReadableMap.toTrackEvent(): BubblTrackEvent =
298
334
  BubblTrackEvent(
299
335
  type = requiredString("type"),
@@ -1,6 +1,7 @@
1
1
  import Foundation
2
2
  import React
3
3
  import BubblSDK
4
+ import UIKit
4
5
 
5
6
  #if canImport(CoreLocation)
6
7
  import CoreLocation
@@ -114,7 +115,11 @@ final class BubblSdk: RCTEventEmitter {
114
115
  reject: @escaping RCTPromiseRejectBlock
115
116
  ) {
116
117
  resolveAsync(resolve, reject) {
117
- try await self.sdk.refreshGeofence(BubblLocation(latitude: latitude, longitude: longitude))
118
+ let location = BubblLocation(latitude: latitude, longitude: longitude)
119
+ try await self.sdk.refreshGeofence(location)
120
+ #if canImport(CoreLocation)
121
+ await self.locationMonitor.refreshBackgroundRegions(near: location)
122
+ #endif
118
123
  return nil
119
124
  }
120
125
  }
@@ -127,7 +132,11 @@ final class BubblSdk: RCTEventEmitter {
127
132
  reject: @escaping RCTPromiseRejectBlock
128
133
  ) {
129
134
  resolveAsync(resolve, reject) {
130
- try await self.sdk.handleLocationUpdate(BubblLocation(latitude: latitude, longitude: longitude))
135
+ let location = BubblLocation(latitude: latitude, longitude: longitude)
136
+ try await self.sdk.handleLocationUpdate(location)
137
+ #if canImport(CoreLocation)
138
+ await self.locationMonitor.refreshBackgroundRegions(near: location)
139
+ #endif
131
140
  return nil
132
141
  }
133
142
  }
@@ -210,6 +219,18 @@ final class BubblSdk: RCTEventEmitter {
210
219
  }
211
220
  }
212
221
 
222
+ @objc(setDefaultNotificationModalStyle:resolver:rejecter:)
223
+ func setDefaultNotificationModalStyle(
224
+ _ style: NSDictionary?,
225
+ resolve: @escaping RCTPromiseResolveBlock,
226
+ reject: @escaping RCTPromiseRejectBlock
227
+ ) {
228
+ resolveAsync(resolve, reject) {
229
+ try await self.sdk.setDefaultNotificationModalStyle(try style?.swiftMap.notificationModalStyle())
230
+ return nil
231
+ }
232
+ }
233
+
213
234
  @objc(registerPushToken:resolver:rejecter:)
214
235
  func registerPushToken(
215
236
  _ token: NSString,
@@ -382,6 +403,31 @@ final class BubblSdk: RCTEventEmitter {
382
403
  private static let eventName = "BubblSdkEvent"
383
404
  }
384
405
 
406
+ @objc(BubblSdkLocationLaunchHandler)
407
+ public final class BubblSdkLocationLaunchHandler: NSObject {
408
+ #if canImport(CoreLocation)
409
+ @MainActor private static let locationMonitor = BubblLocationMonitor(sdk: .shared)
410
+ #endif
411
+
412
+ @objc(handleLaunchOptions:)
413
+ public static func handleLaunchOptions(_ launchOptions: NSDictionary?) -> Bool {
414
+ let launchedForLocation = launchOptions?[UIApplication.LaunchOptionsKey.location.rawValue] != nil
415
+ || launchOptions?[UIApplication.LaunchOptionsKey.location] != nil
416
+
417
+ guard launchedForLocation else {
418
+ return false
419
+ }
420
+
421
+ #if canImport(CoreLocation)
422
+ Task { @MainActor in
423
+ await locationMonitor.resumeForBackgroundLocationLaunch()
424
+ }
425
+ #endif
426
+
427
+ return true
428
+ }
429
+ }
430
+
385
431
  private struct BubblReactNativeError: Error {
386
432
  let code: String
387
433
  let message: String
@@ -457,11 +503,38 @@ private extension Dictionary where Key == String, Value == Any {
457
503
  enableLocationTracking: bool("enableLocationTracking", default: false),
458
504
  notificationRenderingMode: BubblNotificationRenderingMode(rawValue: string("notificationRenderingMode") ?? "sdkDefault") ?? .sdkDefault,
459
505
  enableDefaultNotificationModal: bool("enableDefaultNotificationModal", default: true),
506
+ defaultNotificationModalStyle: try map("defaultNotificationModalStyle")?.notificationModalStyle(),
460
507
  enableDefaultSurveyUi: bool("enableDefaultSurveyUi", default: true),
461
508
  logLevel: BubblLogLevel(rawValue: string("logLevel") ?? "warn") ?? .warn
462
509
  )
463
510
  }
464
511
 
512
+ func notificationModalStyle() throws -> BubblNotificationModalStyle {
513
+ BubblNotificationModalStyle(
514
+ theme: BubblNotificationModalTheme(rawValue: string("theme") ?? "light") ?? .light,
515
+ transparentBackdrop: bool("transparentBackdrop", default: true),
516
+ backdropColor: string("backdropColor"),
517
+ cardBackgroundColor: string("cardBackgroundColor"),
518
+ cardBorderColor: string("cardBorderColor"),
519
+ titleColor: string("titleColor"),
520
+ bodyColor: string("bodyColor"),
521
+ accentColor: string("accentColor"),
522
+ iconBackgroundColor: string("iconBackgroundColor"),
523
+ iconTextColor: string("iconTextColor"),
524
+ primaryButtonBackgroundColor: string("primaryButtonBackgroundColor"),
525
+ primaryButtonTextColor: string("primaryButtonTextColor"),
526
+ secondaryButtonBackgroundColor: string("secondaryButtonBackgroundColor"),
527
+ secondaryButtonTextColor: string("secondaryButtonTextColor"),
528
+ textButtonColor: string("textButtonColor"),
529
+ surveyBackgroundColor: string("surveyBackgroundColor"),
530
+ inputBackgroundColor: string("inputBackgroundColor"),
531
+ inputTextColor: string("inputTextColor"),
532
+ inputBorderColor: string("inputBorderColor"),
533
+ cornerRadius: optionalDouble("cornerRadius"),
534
+ buttonCornerRadius: optionalDouble("buttonCornerRadius")
535
+ )
536
+ }
537
+
465
538
  func trackEvent() throws -> BubblTrackEvent {
466
539
  BubblTrackEvent(
467
540
  type: try requiredString("type"),
@@ -39,6 +39,9 @@ RCT_EXTERN_METHOD(clearCorrelationId:(RCTPromiseResolveBlock)resolve
39
39
  RCT_EXTERN_METHOD(setDefaultNotificationModalEnabled:(BOOL)enabled
40
40
  resolver:(RCTPromiseResolveBlock)resolve
41
41
  rejecter:(RCTPromiseRejectBlock)reject)
42
+ RCT_EXTERN_METHOD(setDefaultNotificationModalStyle:(NSDictionary *)style
43
+ resolver:(RCTPromiseResolveBlock)resolve
44
+ rejecter:(RCTPromiseRejectBlock)reject)
42
45
  RCT_EXTERN_METHOD(registerPushToken:(NSString *)token
43
46
  resolver:(RCTPromiseResolveBlock)resolve
44
47
  rejecter:(RCTPromiseRejectBlock)reject)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bubblsdk/react-native-sdk",
3
- "version": "3.1.6",
3
+ "version": "4.0.0",
4
4
  "description": "React Native wrapper for Bubbl SDK v3 native Android and iOS cores.",
5
5
  "main": "src/index.ts",
6
6
  "module": "src/index.ts",
package/src/index.ts CHANGED
@@ -9,6 +9,31 @@ export type BubblEnvironment = 'development' | 'staging' | 'production';
9
9
  export type BubblLogLevel = 'off' | 'error' | 'warn' | 'info' | 'debug';
10
10
  export type BubblNotificationRenderingMode = 'sdkDefault' | 'hostRendered' | 'eventOnly';
11
11
  export type BubblNotificationSource = 'firebase' | 'apns' | 'runtime' | 'geofence' | 'manual';
12
+ export type BubblNotificationModalTheme = 'light' | 'dark';
13
+
14
+ export type BubblNotificationModalStyle = {
15
+ theme?: BubblNotificationModalTheme;
16
+ transparentBackdrop?: boolean;
17
+ backdropColor?: string | null;
18
+ cardBackgroundColor?: string | null;
19
+ cardBorderColor?: string | null;
20
+ titleColor?: string | null;
21
+ bodyColor?: string | null;
22
+ accentColor?: string | null;
23
+ iconBackgroundColor?: string | null;
24
+ iconTextColor?: string | null;
25
+ primaryButtonBackgroundColor?: string | null;
26
+ primaryButtonTextColor?: string | null;
27
+ secondaryButtonBackgroundColor?: string | null;
28
+ secondaryButtonTextColor?: string | null;
29
+ textButtonColor?: string | null;
30
+ surveyBackgroundColor?: string | null;
31
+ inputBackgroundColor?: string | null;
32
+ inputTextColor?: string | null;
33
+ inputBorderColor?: string | null;
34
+ cornerRadius?: number | null;
35
+ buttonCornerRadius?: number | null;
36
+ };
12
37
 
13
38
  export type BubblConfig = {
14
39
  apiKey: string;
@@ -24,6 +49,7 @@ export type BubblConfig = {
24
49
  enableLocationTracking?: boolean;
25
50
  notificationRenderingMode?: BubblNotificationRenderingMode;
26
51
  enableDefaultNotificationModal?: boolean;
52
+ defaultNotificationModalStyle?: BubblNotificationModalStyle | null;
27
53
  enableDefaultSurveyUi?: boolean;
28
54
  logLevel?: BubblLogLevel;
29
55
  };
@@ -237,6 +263,12 @@ export const Bubbl = {
237
263
  async setDefaultNotificationModalEnabled(enabled: boolean): Promise<void> {
238
264
  return nativeModule().setDefaultNotificationModalEnabled(enabled);
239
265
  },
266
+ async setDefaultNotificationModalStyle(style: BubblNotificationModalStyle | null): Promise<void> {
267
+ return nativeModule().setDefaultNotificationModalStyle(style);
268
+ },
269
+ async resetDefaultNotificationModalStyle(): Promise<void> {
270
+ return nativeModule().setDefaultNotificationModalStyle(null);
271
+ },
240
272
  async disableDefaultNotificationModal(): Promise<void> {
241
273
  return nativeModule().setDefaultNotificationModalEnabled(false);
242
274
  },
@@ -15,6 +15,7 @@ export type BubblBootOptions = {
15
15
  enableLocationTracking?: boolean;
16
16
  notificationRenderingMode?: string;
17
17
  enableDefaultNotificationModal?: boolean;
18
+ defaultNotificationModalStyle?: Object | null;
18
19
  enableDefaultSurveyUi?: boolean;
19
20
  logLevel?: string;
20
21
  };
@@ -34,6 +35,7 @@ export interface Spec extends TurboModule {
34
35
  setCorrelationId(value: string): Promise<void>;
35
36
  clearCorrelationId(): Promise<void>;
36
37
  setDefaultNotificationModalEnabled(enabled: boolean): Promise<void>;
38
+ setDefaultNotificationModalStyle(style?: Object | null): Promise<void>;
37
39
  registerPushToken(token: string): Promise<void>;
38
40
  handleFirebasePayload(payload: Object): Promise<Object | null>;
39
41
  showNotification(payload: Object): Promise<Object>;