@wscsports/blaze-rtn-sdk 1.6.1 → 1.19.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.
Files changed (129) hide show
  1. package/android/build.gradle +1 -1
  2. package/android/src/main/java/com/blaze/rtnblazesdk/BlazeSdkModule.kt +71 -0
  3. package/android/src/main/java/com/blaze/rtnblazesdk/base/BaseWidgetView.kt +133 -1
  4. package/android/src/main/java/com/blaze/rtnblazesdk/customization/BlazeReactLayoutMerger.kt +16 -0
  5. package/android/src/main/java/com/blaze/rtnblazesdk/customization/BlazeReactPlayerTabsStyle.kt +38 -0
  6. package/android/src/main/java/com/blaze/rtnblazesdk/customization/BlazeReactPlayerTabsStyleMerger.kt +63 -0
  7. package/android/src/main/java/com/blaze/rtnblazesdk/customization/BlazeReactStoriesPlayerStyleMerger.kt +49 -0
  8. package/android/src/main/java/com/blaze/rtnblazesdk/customization/BlazeReactStoryPlayerStyle.kt +23 -1
  9. package/android/src/main/java/com/blaze/rtnblazesdk/customization/BlazeReactWidgetLayout.kt +9 -2
  10. package/android/src/main/java/com/blaze/rtnblazesdk/events/MomentsContainerTabsEvent.kt +57 -0
  11. package/android/src/main/java/com/blaze/rtnblazesdk/moments/BlazeMomentsGridView.kt +38 -12
  12. package/android/src/main/java/com/blaze/rtnblazesdk/moments/BlazeMomentsGridViewManager.kt +2 -4
  13. package/android/src/main/java/com/blaze/rtnblazesdk/moments/BlazeMomentsRowView.kt +39 -13
  14. package/android/src/main/java/com/blaze/rtnblazesdk/moments/BlazeMomentsRowViewManager.kt +2 -4
  15. package/android/src/main/java/com/blaze/rtnblazesdk/moments/BlazeMomentsWidgetViewManager.kt +41 -0
  16. package/android/src/main/java/com/blaze/rtnblazesdk/stories/BlazeStoriesGridView.kt +3 -0
  17. package/android/src/main/java/com/blaze/rtnblazesdk/stories/BlazeStoriesRowView.kt +3 -0
  18. package/android/src/main/java/com/blaze/rtnblazesdk/utils/EventParamsBuilder.kt +13 -0
  19. package/android/src/main/java/com/blaze/rtnblazesdk/utils/MomentsWidgetTabsConfigurationExtractUtils.kt +101 -0
  20. package/android/src/main/java/com/blaze/rtnblazesdk/utils/PlaybackConfigurationExtractUtils.kt +31 -0
  21. package/blaze-rtn-sdk.podspec +2 -1
  22. package/ios/Sources/BlazeMomentsGridWidgetManager/BlazeMomentsGridWidgetManager.m +2 -0
  23. package/ios/Sources/BlazeMomentsRowWidgetManager/BlazeMomentsRowWidgetManager.m +2 -0
  24. package/ios/Sources/BlazeNativeSDKModule/BlazeNativeSDKModule.m +18 -0
  25. package/ios/Sources/BlazeNativeSDKModule/BlazeNativeSDKModule.swift +54 -3
  26. package/ios/Sources/LayoutCustomization/StoriesPlayer/BlazeReactStoriesPlayerStyle.swift +19 -0
  27. package/ios/Sources/LayoutCustomization/StoriesPlayer/BlazeReactStoriesPlayerStyleMerger.swift +42 -1
  28. package/ios/Sources/LayoutCustomization/TabsStyle/BlazeReactPlayerTabsStyle.swift +40 -0
  29. package/ios/Sources/LayoutCustomization/TabsStyle/BlazeReactPlayerTabsStyleMerger.swift +70 -0
  30. package/ios/Sources/LayoutCustomization/WidgetLayout/BlazeReactWidgetLayout.swift +6 -0
  31. package/ios/Sources/LayoutCustomization/WidgetLayout/BlazeReactWidgetLayoutMerger.swift +12 -0
  32. package/ios/Sources/Utils/BlazeReactNumberParsing.swift +13 -0
  33. package/ios/Sources/Utils/BlazeReactParamsParser.swift +30 -2
  34. package/ios/Sources/Utils/MomentsWidgetTabsConfigurationExtractUtils.swift +106 -0
  35. package/ios/Sources/Utils/PlaybackConfigurationExtractUtils.swift +35 -4
  36. package/ios/Sources/Widgets/Base/BlazeWidgetBase.swift +200 -32
  37. package/ios/Sources/Widgets/Base/BlazeWidgetViewManagerBase.h +16 -8
  38. package/ios/Sources/Widgets/BlazeMomentsGridWidget.swift +16 -18
  39. package/ios/Sources/Widgets/BlazeMomentsRowWidget.swift +17 -18
  40. package/ios/Sources/Widgets/BlazeStoriesGridWidget.swift +5 -10
  41. package/ios/Sources/Widgets/BlazeStoriesRowWidget.swift +5 -10
  42. package/ios/Sources/Widgets/BlazeVideosGridWidget.swift +6 -17
  43. package/ios/Sources/Widgets/BlazeVideosRowWidget.swift +7 -17
  44. package/lib/commonjs/NativeBlazeSdk.js +12 -0
  45. package/lib/commonjs/NativeBlazeSdk.js.map +1 -1
  46. package/lib/commonjs/classes/player-container-tabs-delegate.js +6 -0
  47. package/lib/commonjs/classes/player-container-tabs-delegate.js.map +1 -0
  48. package/lib/commonjs/index.js.map +1 -1
  49. package/lib/commonjs/interfaces/index.js +11 -0
  50. package/lib/commonjs/interfaces/index.js.map +1 -1
  51. package/lib/commonjs/interfaces/moments-widget-tabs-configuration.js +6 -0
  52. package/lib/commonjs/interfaces/moments-widget-tabs-configuration.js.map +1 -0
  53. package/lib/commonjs/interfaces/player-sound-state.js +2 -0
  54. package/lib/commonjs/interfaces/player-sound-state.js.map +1 -0
  55. package/lib/commonjs/interfaces/stories-playback-configuration.js +2 -0
  56. package/lib/commonjs/interfaces/stories-playback-configuration.js.map +1 -0
  57. package/lib/commonjs/widgets/BlazeBaseWidgetComponent.js +62 -0
  58. package/lib/commonjs/widgets/BlazeBaseWidgetComponent.js.map +1 -1
  59. package/lib/commonjs/widgets/BlazeMomentsGridView.js.map +1 -1
  60. package/lib/commonjs/widgets/BlazeMomentsRowView.js.map +1 -1
  61. package/lib/module/NativeBlazeSdk.js +12 -0
  62. package/lib/module/NativeBlazeSdk.js.map +1 -1
  63. package/lib/module/classes/player-container-tabs-delegate.js +2 -0
  64. package/lib/module/classes/player-container-tabs-delegate.js.map +1 -0
  65. package/lib/module/index.js.map +1 -1
  66. package/lib/module/interfaces/index.js +1 -0
  67. package/lib/module/interfaces/index.js.map +1 -1
  68. package/lib/module/interfaces/moments-widget-tabs-configuration.js +2 -0
  69. package/lib/module/interfaces/moments-widget-tabs-configuration.js.map +1 -0
  70. package/lib/module/interfaces/player-sound-state.js +2 -0
  71. package/lib/module/interfaces/player-sound-state.js.map +1 -0
  72. package/lib/module/interfaces/stories-playback-configuration.js +2 -0
  73. package/lib/module/interfaces/stories-playback-configuration.js.map +1 -0
  74. package/lib/module/widgets/BlazeBaseWidgetComponent.js +62 -0
  75. package/lib/module/widgets/BlazeBaseWidgetComponent.js.map +1 -1
  76. package/lib/module/widgets/BlazeMomentsGridView.js.map +1 -1
  77. package/lib/module/widgets/BlazeMomentsRowView.js.map +1 -1
  78. package/lib/typescript/NativeBlazeSdk.d.ts +6 -0
  79. package/lib/typescript/NativeBlazeSdk.d.ts.map +1 -1
  80. package/lib/typescript/classes/player-container-tabs-delegate.d.ts +18 -0
  81. package/lib/typescript/classes/player-container-tabs-delegate.d.ts.map +1 -0
  82. package/lib/typescript/index.d.ts +4 -1
  83. package/lib/typescript/index.d.ts.map +1 -1
  84. package/lib/typescript/interfaces/actions-options.interface.d.ts +3 -0
  85. package/lib/typescript/interfaces/actions-options.interface.d.ts.map +1 -1
  86. package/lib/typescript/interfaces/index.d.ts +1 -0
  87. package/lib/typescript/interfaces/index.d.ts.map +1 -1
  88. package/lib/typescript/interfaces/moments-playback-configuration.d.ts +7 -0
  89. package/lib/typescript/interfaces/moments-playback-configuration.d.ts.map +1 -1
  90. package/lib/typescript/interfaces/moments-widget-tabs-configuration.d.ts +96 -0
  91. package/lib/typescript/interfaces/moments-widget-tabs-configuration.d.ts.map +1 -0
  92. package/lib/typescript/interfaces/player-sound-state.d.ts +12 -0
  93. package/lib/typescript/interfaces/player-sound-state.d.ts.map +1 -0
  94. package/lib/typescript/interfaces/stories-playback-configuration.d.ts +13 -0
  95. package/lib/typescript/interfaces/stories-playback-configuration.d.ts.map +1 -0
  96. package/lib/typescript/interfaces/stories-player-style.d.ts +23 -1
  97. package/lib/typescript/interfaces/stories-player-style.d.ts.map +1 -1
  98. package/lib/typescript/interfaces/videos-playback-configuration.d.ts +7 -0
  99. package/lib/typescript/interfaces/videos-playback-configuration.d.ts.map +1 -1
  100. package/lib/typescript/interfaces/widget-layout.interface.d.ts +13 -0
  101. package/lib/typescript/interfaces/widget-layout.interface.d.ts.map +1 -1
  102. package/lib/typescript/interfaces/widgets-props.interface.d.ts +9 -0
  103. package/lib/typescript/interfaces/widgets-props.interface.d.ts.map +1 -1
  104. package/lib/typescript/widgets/BlazeBaseWidgetComponent.d.ts +10 -0
  105. package/lib/typescript/widgets/BlazeBaseWidgetComponent.d.ts.map +1 -1
  106. package/lib/typescript/widgets/BlazeBaseWidgetViewProps.d.ts +50 -3
  107. package/lib/typescript/widgets/BlazeBaseWidgetViewProps.d.ts.map +1 -1
  108. package/lib/typescript/widgets/BlazeMomentsGridView.d.ts +1 -2
  109. package/lib/typescript/widgets/BlazeMomentsGridView.d.ts.map +1 -1
  110. package/lib/typescript/widgets/BlazeMomentsRowView.d.ts +1 -2
  111. package/lib/typescript/widgets/BlazeMomentsRowView.d.ts.map +1 -1
  112. package/package.json +1 -1
  113. package/src/NativeBlazeSdk.tsx +26 -0
  114. package/src/classes/player-container-tabs-delegate.tsx +19 -0
  115. package/src/index.tsx +9 -1
  116. package/src/interfaces/actions-options.interface.tsx +3 -0
  117. package/src/interfaces/index.tsx +1 -0
  118. package/src/interfaces/moments-playback-configuration.tsx +8 -0
  119. package/src/interfaces/moments-widget-tabs-configuration.tsx +109 -0
  120. package/src/interfaces/player-sound-state.tsx +11 -0
  121. package/src/interfaces/stories-playback-configuration.tsx +12 -0
  122. package/src/interfaces/stories-player-style.tsx +26 -0
  123. package/src/interfaces/videos-playback-configuration.tsx +8 -0
  124. package/src/interfaces/widget-layout.interface.tsx +14 -0
  125. package/src/interfaces/widgets-props.interface.tsx +10 -0
  126. package/src/widgets/BlazeBaseWidgetComponent.tsx +98 -0
  127. package/src/widgets/BlazeBaseWidgetViewProps.tsx +59 -2
  128. package/src/widgets/BlazeMomentsGridView.tsx +8 -12
  129. package/src/widgets/BlazeMomentsRowView.tsx +8 -12
@@ -6,7 +6,7 @@ buildscript {
6
6
  blaze_default_compileSdkVersion = 35
7
7
  blaze_default_minSdkVersion = 24
8
8
  blaze_default_kotlin_version = '2.0.21'
9
- blazesdk_version = '1.17.1'
9
+ blazesdk_version = '1.19.5'
10
10
  blaze_gson_version = '2.10.1'
11
11
  }
12
12
 
@@ -25,6 +25,7 @@ import com.blaze.blazesdk.features.shared.models.ui_shared.BlazeLinkActionHandle
25
25
  import com.blaze.blazesdk.prefetch.models.BlazeCachingLevel
26
26
  import com.blaze.blazesdk.shared.BlazeSDK
27
27
  import com.blaze.blazesdk.shared.models.BlazeEntryPointTriggerSource
28
+ import com.blaze.blazesdk.shared.models.BlazePlayerSoundState
28
29
  import com.blaze.blazesdk.shared.results.BlazeResult
29
30
  import com.blaze.rtnblazesdk.events.SdkModuleJsEvent
30
31
  import com.blaze.rtnblazesdk.shared.blazeAsyncBridge
@@ -34,6 +35,7 @@ import com.blaze.rtnblazesdk.utils.extractDataSource
34
35
  import com.blaze.rtnblazesdk.utils.extractMomentsPlayerStyle
35
36
  import com.blaze.rtnblazesdk.utils.extractStoriesPlayerStyle
36
37
  import com.blaze.rtnblazesdk.utils.extractMomentsPlaybackConfiguration
38
+ import com.blaze.rtnblazesdk.utils.extractStoriesPlaybackConfiguration
37
39
  import com.blaze.rtnblazesdk.utils.extractVideosPlaybackConfiguration
38
40
  import com.blaze.rtnblazesdk.utils.extractVideosPlayerStyle
39
41
  import com.blaze.rtnblazesdk.utils.getBooleanOrNull
@@ -292,10 +294,14 @@ class BlazeSdkModule(private val context: ReactApplicationContext): ReactContext
292
294
 
293
295
  val triggerSource = options.getString("triggerSource")?.asEntryPointTriggerSource
294
296
  ?: BlazeEntryPointTriggerSource.ENTRYPOINT
297
+ val playbackConfiguration = options.getMap(KEY_ENTRY_POINT_PLAYBACK_CONFIGURATION)
298
+ .extractStoriesPlaybackConfiguration()
299
+ ?: BlazeSDK.getDefaultStoriesPlaybackConfiguration()
295
300
  BlazeSDK.playStory(
296
301
  storyId = storyId,
297
302
  pageId = pageId,
298
303
  storyPlayerStyle = playerStyle,
304
+ playbackConfiguration = playbackConfiguration,
299
305
  triggerSource = triggerSource
300
306
  ) { result ->
301
307
  promise.handleResult(result)
@@ -320,10 +326,14 @@ class BlazeSdkModule(private val context: ReactApplicationContext): ReactContext
320
326
  val triggerSource = options.getString("triggerSource")?.asEntryPointTriggerSource
321
327
  ?: BlazeEntryPointTriggerSource.ENTRYPOINT
322
328
  val shouldOrderContentByReadStatus = options.getBooleanOrNull("shouldOrderContentByReadStatus") ?: true
329
+ val playbackConfiguration = options.getMap(KEY_ENTRY_POINT_PLAYBACK_CONFIGURATION)
330
+ .extractStoriesPlaybackConfiguration()
331
+ ?: BlazeSDK.getDefaultStoriesPlaybackConfiguration()
323
332
  BlazeSDK.playStories(
324
333
  dataSource = dataSource,
325
334
  entryContentId = entryContentId,
326
335
  storyPlayerStyle = playerStyle,
336
+ playbackConfiguration = playbackConfiguration,
327
337
  triggerSource = triggerSource,
328
338
  shouldOrderContentByReadStatus = shouldOrderContentByReadStatus
329
339
  ) { result ->
@@ -558,6 +568,7 @@ class BlazeSdkModule(private val context: ReactApplicationContext): ReactContext
558
568
  putMap("pipConfiguration", Arguments.createMap().apply {
559
569
  putBoolean("enterPipOnAppBackground", config.pip.enterPipOnAppBackground)
560
570
  })
571
+ putDouble("bufferingSpinnerDelayMs", config.bufferingSpinnerDelayMs.toDouble())
561
572
  }
562
573
  promise.resolve(result)
563
574
  } catch (e: Exception) {
@@ -600,6 +611,7 @@ class BlazeSdkModule(private val context: ReactApplicationContext): ReactContext
600
611
  }
601
612
  }
602
613
  putMap("loopBehavior", loopBehavior)
614
+ putDouble("bufferingSpinnerDelayMs", config.bufferingSpinnerDelayMs.toDouble())
603
615
  }
604
616
  promise.resolve(result)
605
617
  } catch (e: Exception) {
@@ -607,6 +619,65 @@ class BlazeSdkModule(private val context: ReactApplicationContext): ReactContext
607
619
  }
608
620
  }
609
621
 
622
+ @ReactMethod
623
+ @DoNotStrip
624
+ fun setDefaultStoriesPlaybackConfiguration(config: ReadableMap, promise: Promise) {
625
+ try {
626
+ val playbackConfig = (config as ReadableMap?).extractStoriesPlaybackConfiguration()
627
+ ?: run {
628
+ promise.rejectWith("Invalid stories playback configuration")
629
+ return
630
+ }
631
+ BlazeSDK.setDefaultStoriesPlaybackConfiguration(playbackConfig)
632
+ promise.resolve(null)
633
+ } catch (e: Exception) {
634
+ promise.rejectWith(e.toString())
635
+ }
636
+ }
637
+
638
+ @ReactMethod
639
+ @DoNotStrip
640
+ fun getDefaultStoriesPlaybackConfiguration(promise: Promise) {
641
+ try {
642
+ val config = BlazeSDK.getDefaultStoriesPlaybackConfiguration()
643
+ val result = Arguments.createMap().apply {
644
+ putDouble("bufferingSpinnerDelayMs", config.bufferingSpinnerDelayMs.toDouble())
645
+ }
646
+ promise.resolve(result)
647
+ } catch (e: Exception) {
648
+ promise.rejectWith(e.toString())
649
+ }
650
+ }
651
+
652
+ @ReactMethod
653
+ @DoNotStrip
654
+ fun setPlayerSoundState(state: String, promise: Promise) {
655
+ try {
656
+ val nativeState = when (state.lowercase()) {
657
+ "mute" -> BlazePlayerSoundState.MUTE
658
+ "unmute" -> BlazePlayerSoundState.UNMUTE
659
+ else -> {
660
+ promise.rejectWith("Invalid BlazePlayerSoundState '$state'. Expected 'mute' or 'unmute'.")
661
+ return
662
+ }
663
+ }
664
+ BlazeSDK.setPlayerSoundState(nativeState)
665
+ promise.resolve(null)
666
+ } catch (e: Exception) {
667
+ promise.rejectWith(e.toString())
668
+ }
669
+ }
670
+
671
+ @ReactMethod
672
+ @DoNotStrip
673
+ fun isMuted(promise: Promise) {
674
+ try {
675
+ promise.resolve(BlazeSDK.isMuted)
676
+ } catch (e: Exception) {
677
+ promise.rejectWith(e.toString())
678
+ }
679
+ }
680
+
610
681
  @ReactMethod
611
682
  @DoNotStrip
612
683
  fun dismissPlayer(promise: Promise) {
@@ -6,6 +6,7 @@ import android.util.AttributeSet
6
6
  import android.view.View
7
7
  import androidx.core.view.doOnPreDraw
8
8
  import com.blaze.blazesdk.data_source.BlazeDataSourceType
9
+ import com.blaze.blazesdk.delegates.BlazePlayerContainerTabsDelegate
9
10
  import com.blaze.blazesdk.delegates.BlazeWidgetDelegate
10
11
  import com.blaze.blazesdk.delegates.models.BlazeCTAActionType
11
12
  import com.blaze.blazesdk.delegates.models.BlazePlayerEvent
@@ -21,6 +22,8 @@ import com.blaze.rtnblazesdk.BlazeSdkModuleRepo
21
22
  import com.blaze.rtnblazesdk.customization.BlazeReactWidgetItemCustomMapping
22
23
  import com.blaze.rtnblazesdk.customization.BlazeReactWidgetItemStyleOverrides
23
24
  import com.blaze.rtnblazesdk.customization.mergedWith
25
+ import com.blaze.rtnblazesdk.events.MomentsContainerTabsEvent
26
+ import com.blaze.rtnblazesdk.events.MomentsContainerTabsEventNames
24
27
  import com.blaze.rtnblazesdk.events.WidgetCTAClickEvent
25
28
  import com.blaze.rtnblazesdk.events.WidgetDataLoadCompletedEvent
26
29
  import com.blaze.rtnblazesdk.events.WidgetDataLoadStartedEvent
@@ -32,6 +35,7 @@ import com.blaze.rtnblazesdk.events.WidgetOnTriggerPlayerBodyTextLinkEvent
32
35
  import com.blaze.rtnblazesdk.events.WidgetPlayerDidAppearEvent
33
36
  import com.blaze.rtnblazesdk.events.WidgetPlayerDismissedEvent
34
37
  import com.blaze.blazesdk.style.shared.models.BlazePlayerCustomActionButtonParams
38
+ import com.blaze.rtnblazesdk.utils.EventParamsBuilder
35
39
  import com.blaze.rtnblazesdk.utils.extractCachingLevel
36
40
  import com.blaze.rtnblazesdk.utils.extractDataSource
37
41
  import com.blaze.rtnblazesdk.utils.getBooleanOrNull
@@ -41,6 +45,7 @@ import com.blaze.rtnblazesdk.utils.parsing.toObject
41
45
  import com.facebook.react.bridge.ReactContext
42
46
  import com.facebook.react.bridge.ReadableArray
43
47
  import com.facebook.react.bridge.ReadableMap
48
+ import com.facebook.react.bridge.WritableMap
44
49
  import com.facebook.react.uimanager.ReactStylesDiffMap
45
50
  import com.facebook.react.uimanager.UIManagerHelper
46
51
  import com.facebook.react.uimanager.events.Event
@@ -65,6 +70,12 @@ abstract class BaseWidgetView @JvmOverloads constructor(
65
70
  var nativeWidgetLayout: BlazeWidgetLayout? = null
66
71
  var reactWidgetPlaybackConfigurationMap: ReadableMap? = null
67
72
 
73
+ /**
74
+ * Moments-only: when set, the widget is initialized to launch a fullscreen tabs player on item
75
+ * tap. Exported to the bridge only by the Moments row/grid managers; ignored by Stories/Videos.
76
+ */
77
+ var reactTabsContainerMap: ReadableMap? = null
78
+
68
79
  val cachingLevel: BlazeCachingLevel
69
80
  get() = reactCachingLevel?.extractCachingLevel() ?: BlazeSdkModuleRepo.cachingLevel ?: BlazeCachingLevel.DEFAULT
70
81
 
@@ -203,6 +214,120 @@ abstract class BaseWidgetView @JvmOverloads constructor(
203
214
  }
204
215
  }
205
216
 
217
+ /**
218
+ * Per-view delegate for the Moments "widget to tabs" fullscreen player. Passed to the native tabs
219
+ * configuration by the Moments row/grid views. Every callback is dispatched on this view's own
220
+ * event channel, so each widget reports to its own JS `momentsContainerTabsDelegate` — mirroring
221
+ * the per-instance [widgetDelegate].
222
+ */
223
+ protected val containerTabsDelegate =
224
+ object : BlazePlayerContainerTabsDelegate {
225
+ override fun onDataLoadStarted(playerType: BlazePlayerType, sourceId: String?) {
226
+ dispatchTabsEvent(
227
+ MomentsContainerTabsEventNames.DATA_LOAD_STARTED,
228
+ EventParamsBuilder.buildDataLoadStartedParams(playerType, sourceId)
229
+ )
230
+ }
231
+
232
+ override fun onDataLoadComplete(
233
+ playerType: BlazePlayerType,
234
+ sourceId: String?,
235
+ itemsCount: Int,
236
+ result: BlazeResult<Unit>
237
+ ) {
238
+ dispatchTabsEvent(
239
+ MomentsContainerTabsEventNames.DATA_LOAD_COMPLETE,
240
+ EventParamsBuilder.buildDataLoadCompletedParams(playerType, sourceId, itemsCount, result)
241
+ )
242
+ }
243
+
244
+ override fun onPlayerDidAppear(playerType: BlazePlayerType, sourceId: String?) {
245
+ dispatchTabsEvent(
246
+ MomentsContainerTabsEventNames.PLAYER_DID_APPEAR,
247
+ EventParamsBuilder.buildPlayerDidAppearParams(playerType, sourceId)
248
+ )
249
+ }
250
+
251
+ override fun onPlayerDidDismiss(playerType: BlazePlayerType, sourceId: String?) {
252
+ dispatchTabsEvent(
253
+ MomentsContainerTabsEventNames.PLAYER_DID_DISMISS,
254
+ EventParamsBuilder.buildPlayerDidDismissParams(playerType, sourceId)
255
+ )
256
+ }
257
+
258
+ override fun onTriggerCTA(
259
+ playerType: BlazePlayerType,
260
+ sourceId: String?,
261
+ actionType: BlazeCTAActionType,
262
+ actionParam: String
263
+ ): Boolean {
264
+ dispatchTabsEvent(
265
+ MomentsContainerTabsEventNames.TRIGGER_CTA,
266
+ EventParamsBuilder.buildTriggerCTAParams(playerType, sourceId, actionType, actionParam)
267
+ )
268
+ return appOverridesCTAHandling
269
+ }
270
+
271
+ override fun onTriggerPlayerBodyTextLink(
272
+ playerType: BlazePlayerType,
273
+ sourceId: String?,
274
+ actionParam: String
275
+ ): BlazeLinkActionHandleType {
276
+ dispatchTabsEvent(
277
+ MomentsContainerTabsEventNames.TRIGGER_PLAYER_BODY_TEXT_LINK,
278
+ EventParamsBuilder.buildTriggerPlayerBodyTextLinkParams(playerType, sourceId, actionParam)
279
+ )
280
+ return BlazeLinkActionHandleType.DEEPLINK
281
+ }
282
+
283
+ override fun onPlayerEventTriggered(
284
+ playerType: BlazePlayerType,
285
+ sourceId: String?,
286
+ event: BlazePlayerEvent
287
+ ) {
288
+ dispatchTabsEvent(
289
+ MomentsContainerTabsEventNames.PLAYER_EVENT_TRIGGERED,
290
+ EventParamsBuilder.buildPlayerEventTriggeredParams(playerType, sourceId, event)
291
+ )
292
+ }
293
+
294
+ override fun onTriggerCustomActionButton(
295
+ playerType: BlazePlayerType,
296
+ sourceId: String?,
297
+ customParams: BlazePlayerCustomActionButtonParams
298
+ ) {
299
+ dispatchTabsEvent(
300
+ MomentsContainerTabsEventNames.TRIGGER_CUSTOM_ACTION_BUTTON,
301
+ EventParamsBuilder.buildTriggerCustomActionButtonParams(playerType, sourceId, customParams)
302
+ )
303
+ }
304
+
305
+ override fun onTabSelected(
306
+ playerType: BlazePlayerType,
307
+ sourceId: String?,
308
+ tabIndex: Int
309
+ ) {
310
+ dispatchTabsEvent(
311
+ MomentsContainerTabsEventNames.TAB_SELECTED,
312
+ EventParamsBuilder.buildTabSelectedParams(playerType, sourceId, tabIndex)
313
+ )
314
+ }
315
+
316
+ // `onSearchButtonClicked` (sync return) and `onShareClicked` (not bridged anywhere) are
317
+ // intentionally left at their native defaults.
318
+ }
319
+
320
+ private fun dispatchTabsEvent(name: String, data: WritableMap) {
321
+ dispatchEvent(
322
+ MomentsContainerTabsEvent(
323
+ UIManagerHelper.getSurfaceId(context),
324
+ id,
325
+ name = name,
326
+ data = data,
327
+ )
328
+ )
329
+ }
330
+
206
331
  abstract fun callInitWidget(dataSource: BlazeDataSourceType)
207
332
  abstract fun createNewWidgetView(): View
208
333
  abstract fun reloadData(isSilentRefresh: Boolean)
@@ -247,13 +372,20 @@ abstract class BaseWidgetView @JvmOverloads constructor(
247
372
  )
248
373
  this.addView(view)
249
374
  view.doOnPreDraw {
250
- dataSourceType?.let { sourceType ->
375
+ resolveInitialDataSource()?.let { sourceType ->
251
376
  callInitWidget(sourceType)
252
377
  }
253
378
  }
254
379
  }
255
380
  }
256
381
 
382
+ /**
383
+ * The data source used to trigger [callInitWidget]. Defaults to the explicit `dataSource` prop.
384
+ * Moments overrides this so a tabs-backed widget initializes from its first tab even when no
385
+ * standalone `dataSource` prop is provided.
386
+ */
387
+ protected open fun resolveInitialDataSource(): BlazeDataSourceType? = dataSourceType
388
+
257
389
  private fun removeWidgetView() {
258
390
  widgetView?.let {
259
391
  removeView(widgetView)
@@ -19,6 +19,7 @@ import BlazeReactWidgetItemStatusIndicatorStyle
19
19
  import BlazeReactWidgetItemStyle
20
20
  import BlazeReactWidgetItemTitleStyle
21
21
  import BlazeReactWidgetLayout
22
+ import BlazeReactWidgetShimmeringStyle
22
23
  import android.content.Context
23
24
  import com.blaze.blazesdk.style.shared.models.BlazeObjectPositioning
24
25
  import com.blaze.blazesdk.style.shared.models.BlazeObjectXPosition
@@ -37,6 +38,7 @@ import com.blaze.blazesdk.style.widgets.BlazeWidgetItemStyle
37
38
  import com.blaze.blazesdk.style.widgets.BlazeWidgetItemTextStyle
38
39
  import com.blaze.blazesdk.style.widgets.BlazeWidgetItemTitleStyle
39
40
  import com.blaze.blazesdk.style.widgets.BlazeWidgetLayout
41
+ import com.blaze.blazesdk.style.widgets.BlazeWidgetShimmeringStyle
40
42
 
41
43
  fun BlazeWidgetLayout.mergedWith(
42
44
  customization: BlazeReactWidgetLayout?,
@@ -55,6 +57,20 @@ fun BlazeWidgetLayout.mergedWith(
55
57
  merged.widgetItemStyle =
56
58
  this.widgetItemStyle.mergedWith(customization.widgetItemStyle, context)
57
59
  merged.margins = this.margins.mergedWith(customization.margins)
60
+ merged.shimmering = this.shimmering.mergedWith(customization.shimmering)
61
+ return merged
62
+ }
63
+
64
+ fun BlazeWidgetShimmeringStyle.mergedWith(
65
+ customization: BlazeReactWidgetShimmeringStyle?
66
+ ): BlazeWidgetShimmeringStyle {
67
+ customization ?: return this
68
+
69
+ val merged = this
70
+
71
+ merged.baseColor = safeParseColor(customization.baseColor) ?: merged.baseColor
72
+ merged.highlightColor = safeParseColor(customization.highlightColor) ?: merged.highlightColor
73
+
58
74
  return merged
59
75
  }
60
76
 
@@ -0,0 +1,38 @@
1
+ import androidx.annotation.Keep
2
+
3
+ // JSON-serializable mirror of the JS `BlazePlayerTabsStyle` (see
4
+ // `moments-widget-tabs-configuration.tsx`). Deserialized with `toObject<...>()` and merged onto the
5
+ // native `BlazePlayerTabsStyle.base()` — same approach as the player-style customization, so we
6
+ // don't parse the `ReadableMap` field-by-field.
7
+
8
+ @Keep
9
+ data class BlazeReactPlayerTabsStyle(
10
+ val padding: BlazeReactMargins?,
11
+ val gradient: BlazeReactPlayerTabsGradientStyle?,
12
+ val icon: BlazeReactPlayerTabItemIconStyle?,
13
+ val selectedTabState: BlazeReactPlayerTabItemStyle?,
14
+ val unselectedTabState: BlazeReactPlayerTabItemStyle?,
15
+ )
16
+
17
+ @Keep
18
+ data class BlazeReactPlayerTabsGradientStyle(
19
+ val isVisible: Boolean?,
20
+ val startColor: String?, // Hex
21
+ val middleColor: String?, // Hex
22
+ val endColor: String?, // Hex
23
+ )
24
+
25
+ @Keep
26
+ data class BlazeReactPlayerTabItemIconStyle(
27
+ val iconTint: String?, // Hex
28
+ val padding: Int?,
29
+ )
30
+
31
+ @Keep
32
+ data class BlazeReactPlayerTabItemStyle(
33
+ val font: BlazeReactTitleFont?,
34
+ val textColor: String?, // Hex
35
+ val textSize: Float?,
36
+ val letterSpacing: Float?,
37
+ val lineHeight: Int?,
38
+ )
@@ -0,0 +1,63 @@
1
+ package com.blaze.rtnblazesdk.customization
2
+
3
+ import BlazeReactPlayerTabItemStyle
4
+ import BlazeReactPlayerTabsStyle
5
+ import android.content.Context
6
+ import com.blaze.blazesdk.style.players.tabs.BlazePlayerTabItemStyle
7
+ import com.blaze.blazesdk.style.players.tabs.BlazePlayerTabsStyle
8
+ import com.blaze.blazesdk.style.shared.models.blazeDp
9
+
10
+ /**
11
+ * Merges the JS-supplied [BlazeReactPlayerTabsStyle] onto a native [BlazePlayerTabsStyle] (typically
12
+ * `BlazePlayerTabsStyle.base()`). Every field is optional; omitted fields keep their native default.
13
+ * Mirrors the player-style mergers in this package.
14
+ */
15
+ fun BlazePlayerTabsStyle.mergedWith(
16
+ customization: BlazeReactPlayerTabsStyle?,
17
+ context: Context
18
+ ): BlazePlayerTabsStyle {
19
+ customization ?: return this
20
+
21
+ val merged = this
22
+
23
+ merged.padding.mergedWith(customization.padding)
24
+
25
+ customization.gradient?.let { gradient ->
26
+ gradient.isVisible?.let { merged.gradient.isVisible = it }
27
+ safeParseColor(gradient.startColor)?.let { merged.gradient.startColor = it }
28
+ safeParseColor(gradient.middleColor)?.let { merged.gradient.middleColor = it }
29
+ safeParseColor(gradient.endColor)?.let { merged.gradient.endColor = it }
30
+ }
31
+
32
+ customization.icon?.let { icon ->
33
+ merged.icon?.let { iconStyle ->
34
+ safeParseColor(icon.iconTint)?.let { iconStyle.iconTint = it }
35
+ icon.padding?.let { iconStyle.padding = it.blazeDp }
36
+ }
37
+ }
38
+
39
+ merged.selectedTabState.mergedWith(customization.selectedTabState, context)
40
+ merged.unselectedTabState.mergedWith(customization.unselectedTabState, context)
41
+
42
+ return merged
43
+ }
44
+
45
+ private fun BlazePlayerTabItemStyle.mergedWith(
46
+ customization: BlazeReactPlayerTabItemStyle?,
47
+ context: Context
48
+ ): BlazePlayerTabItemStyle {
49
+ customization ?: return this
50
+
51
+ val merged = this
52
+
53
+ customization.font?.toFontResId(context)?.let { merged.fontResId = it }
54
+ safeParseColor(customization.textColor)?.let { merged.textColor = it }
55
+ customization.textSize?.let { merged.textSize = it }
56
+ // JS letterSpacing is in points (like RN/iOS); Android's TextView expects em, so divide by textSize.
57
+ customization.letterSpacing?.let { letterSpacingPt ->
58
+ merged.letterSpacing = if (merged.textSize > 0f) letterSpacingPt / merged.textSize else letterSpacingPt
59
+ }
60
+ customization.lineHeight?.let { merged.lineHeight = it.blazeDp }
61
+
62
+ return merged
63
+ }
@@ -11,11 +11,15 @@ import BlazeReactStoryPlayerHeaderGradientStyle
11
11
  import BlazeReactStoryPlayerLastUpdateTextStyle
12
12
  import BlazeReactStoryPlayerProgressBarStyle
13
13
  import BlazeReactStoryPlayerStyle
14
+ import BlazeReactStoryPlayerTitleImageSource
15
+ import BlazeReactStoryPlayerTitleImageStaticSource
16
+ import BlazeReactStoryPlayerTitleImageStyle
14
17
  import BlazeReactStoryPlayerTitleTextStyle
15
18
  import BlazeReactTextCase
16
19
  import android.content.Context
17
20
  import com.blaze.blazesdk.style.players.BlazeTextCase
18
21
  import com.blaze.blazesdk.style.players.stories.BlazeStoryPlayerButtonsStyle
22
+ import com.blaze.blazesdk.style.players.stories.BlazeStoryPlayerTitleImageStyle
19
23
  import com.blaze.blazesdk.style.players.stories.BlazeStoryPlayerChipStyle
20
24
  import com.blaze.blazesdk.style.players.stories.BlazeStoryPlayerChipsStyle
21
25
  import com.blaze.blazesdk.style.players.stories.BlazeStoryPlayerCtaStyle
@@ -95,10 +99,55 @@ fun BlazeStoryPlayerTitleTextStyle.mergedWith(
95
99
  merged.textSize = customization.textSize ?: merged.textSize
96
100
  merged.textColor = safeParseColor(customization.textColor) ?: this.textColor
97
101
  merged.isVisible = customization.isVisible ?: this.isVisible
102
+ merged.image = merged.image.mergedWith(customization.image, context)
98
103
 
99
104
  return merged
100
105
  }
101
106
 
107
+ fun BlazeStoryPlayerTitleImageStyle.mergedWith(
108
+ customization: BlazeReactStoryPlayerTitleImageStyle?,
109
+ context: Context
110
+ ): BlazeStoryPlayerTitleImageStyle {
111
+ customization ?: return this
112
+
113
+ val merged = this
114
+ merged.isVisible = customization.isVisible ?: this.isVisible
115
+ merged.size = customization.size?.toInt()?.blazeDp ?: this.size
116
+
117
+ customization.source?.let { reactSource ->
118
+ when (reactSource.type) {
119
+ "static" -> {
120
+ reactSource.staticSource?.toNativeStaticSource(context)?.let {
121
+ merged.source = it
122
+ }
123
+ }
124
+ "dynamic" -> {
125
+ merged.source = BlazeStoryPlayerTitleImageStyle.BlazeStoryPlayerTitleImageSource.Dynamic(
126
+ fallback = reactSource.fallback?.toNativeStaticSource(context)
127
+ )
128
+ }
129
+ }
130
+ }
131
+
132
+ return merged
133
+ }
134
+
135
+ private fun BlazeReactStoryPlayerTitleImageStaticSource.toNativeStaticSource(
136
+ context: Context
137
+ ): BlazeStoryPlayerTitleImageStyle.BlazeStoryPlayerTitleImageSource.Static? {
138
+ return when (type) {
139
+ "image" -> {
140
+ val resId = image?.toImageResId(context) ?: return null
141
+ BlazeStoryPlayerTitleImageStyle.BlazeStoryPlayerTitleImageSource.Static.ResId(resId)
142
+ }
143
+ "url" -> {
144
+ url ?: return null
145
+ BlazeStoryPlayerTitleImageStyle.BlazeStoryPlayerTitleImageSource.Static.Url(url)
146
+ }
147
+ else -> null
148
+ }
149
+ }
150
+
102
151
  fun BlazeStoryPlayerLastUpdateTextStyle.mergedWith(
103
152
  customization: BlazeReactStoryPlayerLastUpdateTextStyle?,
104
153
  context: Context
@@ -23,7 +23,29 @@ data class BlazeReactStoryPlayerTitleTextStyle(
23
23
  val font: BlazeReactTitleFont?,
24
24
  val textSize: Float?,
25
25
  val textColor: String?,
26
- var isVisible: Boolean?
26
+ var isVisible: Boolean?,
27
+ val image: BlazeReactStoryPlayerTitleImageStyle?
28
+ )
29
+
30
+ @Keep
31
+ data class BlazeReactStoryPlayerTitleImageStyle(
32
+ val isVisible: Boolean?,
33
+ val size: Float?,
34
+ val source: BlazeReactStoryPlayerTitleImageSource?
35
+ )
36
+
37
+ @Keep
38
+ data class BlazeReactStoryPlayerTitleImageSource(
39
+ val type: String?,
40
+ val staticSource: BlazeReactStoryPlayerTitleImageStaticSource?,
41
+ val fallback: BlazeReactStoryPlayerTitleImageStaticSource?
42
+ )
43
+
44
+ @Keep
45
+ data class BlazeReactStoryPlayerTitleImageStaticSource(
46
+ val type: String?,
47
+ val image: BlazeReactImage?,
48
+ val url: String?
27
49
  )
28
50
 
29
51
  @Keep
@@ -278,5 +278,12 @@ data class BlazeReactWidgetLayout(
278
278
  val columns: Int?,
279
279
  val maxDisplayItemsCount: Int?,
280
280
  val widgetItemStyle: BlazeReactWidgetItemStyle?,
281
- val margins: BlazeReactMargins?
282
- )
281
+ val margins: BlazeReactMargins?,
282
+ val shimmering: BlazeReactWidgetShimmeringStyle?
283
+ )
284
+
285
+ @Keep
286
+ data class BlazeReactWidgetShimmeringStyle(
287
+ val baseColor: String?,
288
+ val highlightColor: String?
289
+ )
@@ -0,0 +1,57 @@
1
+ package com.blaze.rtnblazesdk.events
2
+
3
+ import com.facebook.react.bridge.WritableMap
4
+ import com.facebook.react.uimanager.events.Event
5
+
6
+ /**
7
+ *
8
+ * One delegate is created per [com.blaze.rtnblazesdk.base.BaseWidgetView]; its callbacks are
9
+ * dispatched on that view's own event channel (mirroring the per-instance `widgetDelegate` events),
10
+ * so each widget reports to its own JS `momentsContainerTabsDelegate`.
11
+ *
12
+ * A single event class carries every callback (distinguished by [name]) to avoid nine near-identical
13
+ * classes; the native names are mapped to the JS `on...Internal` props by the Moments view managers
14
+ * via [MomentsContainerTabsEventNames.registrationMap].
15
+ */
16
+ class MomentsContainerTabsEvent(
17
+ surfaceId: Int,
18
+ viewTag: Int,
19
+ private val name: String,
20
+ private val data: WritableMap?,
21
+ ) : Event<MomentsContainerTabsEvent>(surfaceId, viewTag) {
22
+
23
+ override fun getEventName(): String = name
24
+
25
+ override fun getEventData(): WritableMap? = data
26
+ }
27
+
28
+ /**
29
+ * The native names only need to be unique; the JS `registrationName` is the `on...Internal` prop the
30
+ * `BlazeMomentsRowView` / `BlazeMomentsGridView` components wire when a `momentsContainerTabsDelegate`
31
+ * is provided. Shared by [com.blaze.rtnblazesdk.base.BaseWidgetView] (dispatch) and the Moments view
32
+ * managers (registration).
33
+ */
34
+ object MomentsContainerTabsEventNames {
35
+
36
+ const val TAB_SELECTED = "Blaze.MomentsContainerTabsTabSelectedEvent"
37
+ const val DATA_LOAD_STARTED = "Blaze.MomentsContainerTabsDataLoadStartedEvent"
38
+ const val DATA_LOAD_COMPLETE = "Blaze.MomentsContainerTabsDataLoadCompleteEvent"
39
+ const val PLAYER_DID_APPEAR = "Blaze.MomentsContainerTabsPlayerDidAppearEvent"
40
+ const val PLAYER_DID_DISMISS = "Blaze.MomentsContainerTabsPlayerDidDismissEvent"
41
+ const val TRIGGER_CTA = "Blaze.MomentsContainerTabsTriggerCTAEvent"
42
+ const val TRIGGER_PLAYER_BODY_TEXT_LINK = "Blaze.MomentsContainerTabsTriggerPlayerBodyTextLinkEvent"
43
+ const val PLAYER_EVENT_TRIGGERED = "Blaze.MomentsContainerTabsPlayerEventTriggeredEvent"
44
+ const val TRIGGER_CUSTOM_ACTION_BUTTON = "Blaze.MomentsContainerTabsTriggerCustomActionButtonEvent"
45
+
46
+ val registrationMap: Map<String, String> = mapOf(
47
+ TAB_SELECTED to "onMomentsContainerTabsTabSelectedInternal",
48
+ DATA_LOAD_STARTED to "onMomentsContainerTabsDataLoadStartedInternal",
49
+ DATA_LOAD_COMPLETE to "onMomentsContainerTabsDataLoadCompletedInternal",
50
+ PLAYER_DID_APPEAR to "onMomentsContainerTabsPlayerDidAppearInternal",
51
+ PLAYER_DID_DISMISS to "onMomentsContainerTabsPlayerDismissedInternal",
52
+ TRIGGER_CTA to "onMomentsContainerTabsTriggerCTAInternal",
53
+ TRIGGER_PLAYER_BODY_TEXT_LINK to "onMomentsContainerTabsTriggerPlayerBodyTextLinkInternal",
54
+ PLAYER_EVENT_TRIGGERED to "onMomentsContainerTabsPlayerEventTriggeredInternal",
55
+ TRIGGER_CUSTOM_ACTION_BUTTON to "onMomentsContainerTabsTriggerCustomActionButtonInternal",
56
+ )
57
+ }