react-native-firework-sdk 2.0.0 → 2.1.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 (138) hide show
  1. package/android/build.gradle +5 -3
  2. package/android/src/main/java/com/fireworksdk/bridge/components/videofeed/StoryBlockFragment.kt +129 -0
  3. package/android/src/main/java/com/fireworksdk/bridge/components/videofeed/StoryBlockFrameLayout.kt +84 -0
  4. package/android/src/main/java/com/fireworksdk/bridge/models/FWEventName.kt +2 -1
  5. package/android/src/main/java/com/fireworksdk/bridge/models/FWProductInfoViewConfiguration.kt +18 -5
  6. package/android/src/main/java/com/fireworksdk/bridge/models/FWProductInfoViewConfigurationDeserializer.kt +33 -9
  7. package/android/src/main/java/com/fireworksdk/bridge/models/FWShoppingCtaResult.kt +17 -0
  8. package/android/src/main/java/com/fireworksdk/bridge/models/FWShoppingCtaResultDeserializer.kt +33 -0
  9. package/android/src/main/java/com/fireworksdk/bridge/reactnative/manager/FWStoryBlockManager.kt +126 -45
  10. package/android/src/main/java/com/fireworksdk/bridge/reactnative/manager/FWVideoFeedManager.kt +6 -3
  11. package/android/src/main/java/com/fireworksdk/bridge/reactnative/models/FWVideoShoppingInterface.kt +2 -2
  12. package/android/src/main/java/com/fireworksdk/bridge/reactnative/module/FWNavigatorModule.kt +9 -1
  13. package/android/src/main/java/com/fireworksdk/bridge/reactnative/module/FWVideoShoppingModule.kt +81 -50
  14. package/android/src/main/java/com/fireworksdk/bridge/reactnative/utils/FWEventUtils.kt +9 -2
  15. package/android/src/main/java/com/fireworksdk/bridge/utils/FWConfigUtil.kt +1 -1
  16. package/android/src/main/java/com/fireworksdk/bridge/utils/FWGlobalDataUtil.kt +4 -0
  17. package/android/src/main/java/com/fireworksdk/bridge/utils/FWLanguageUtil.kt +48 -16
  18. package/android/src/main/res/layout/fw_bridge_story_block.xml +24 -0
  19. package/ios/Components/StoryBlock.swift +33 -2
  20. package/ios/Components/StoryBlockManager.m +32 -0
  21. package/ios/Components/VideoFeed.swift +9 -29
  22. package/ios/Components/VideoFeedManager.m +11 -6
  23. package/ios/FireworkSdk.xcodeproj/project.pbxproj +378 -104
  24. package/ios/Models/NativeToRN/FireworkEventName.swift +3 -1
  25. package/ios/Models/RNToNative/RCTConvert+Shopping.swift +21 -0
  26. package/ios/Models/RNToNative/RCTConvert+VideoFeed.swift +27 -0
  27. package/ios/Modules/FWNavigatorModule/FWNavigatorModule.swift +5 -1
  28. package/ios/Modules/FireworkSDKModule/FireworkSDKModule.m +1 -0
  29. package/ios/Modules/FireworkSDKModule/FireworkSDKModule.swift +30 -0
  30. package/ios/Modules/Shopping/ProductInfoViewConfiguration.swift +13 -0
  31. package/ios/Modules/Shopping/ShoppingCTAResult.swift +16 -0
  32. package/ios/Modules/Shopping/ShoppingModule.m +2 -1
  33. package/ios/Modules/Shopping/ShoppingModule.swift +106 -30
  34. package/ios/Support/MultiHostStreaming/FWMultiHostStreaming.podspec +24 -0
  35. package/ios/Support/MultiHostStreaming/src/MultiHostStreamingSDK.swift +17 -0
  36. package/ios/Utils/AppLanguage/Bundle+FWSwizzle.swift +58 -0
  37. package/ios/Utils/AppLanguage/FWAppLanguageManager.swift +139 -0
  38. package/ios/Utils/AppLanguage/FWLanguageUtil.swift +43 -0
  39. package/ios/Utils/AppLanguage/NumberFormatter+FWSwizzle.swift +25 -0
  40. package/ios/Utils/AppLanguage/UIImageView+FWSwizzle.swift +91 -0
  41. package/ios/Utils/AppLanguage/UILabel+FWSwizzle.swift +98 -0
  42. package/ios/Utils/AppLanguage/UITextField+FWSwizzle.swift +97 -0
  43. package/ios/Utils/AppLanguage/UITextView+FWSwizzle.swift +97 -0
  44. package/ios/Utils/AppLanguage/UIView+FWSwizzle.swift +38 -0
  45. package/ios/Utils/AppLanguage/UIViewController+FWSwizzle.swift +32 -0
  46. package/ios/Utils/AppLanguage/UIWindow+FWSwizzle.swift +26 -0
  47. package/ios/Utils/AppLanguage/URLSession+FWSwizzle.swift +69 -0
  48. package/ios/Utils/{DispatchQueue+FWOnce.swift → Extensions/DispatchQueue+FWOnce.swift} +3 -3
  49. package/ios/Utils/{UINavigationController+FWSwizzle.swift → Extensions/Swizzle/UINavigationController+FWSwizzle.swift} +6 -8
  50. package/ios/Utils/Extensions/UIView+FWUIHierarchy.swift +47 -0
  51. package/ios/Utils/FWRTL/Classes/Manager/FWRTLManager.h +25 -0
  52. package/ios/Utils/FWRTL/Classes/Manager/FWRTLManager.m +75 -0
  53. package/ios/Utils/FWRTL/Classes/UICategories/CALayer+FWRTL.h +21 -0
  54. package/ios/Utils/FWRTL/Classes/UICategories/CALayer+FWRTL.m +124 -0
  55. package/ios/Utils/FWRTL/Classes/UICategories/FWRTLRemoteViewControllerAdaptor.h +11 -0
  56. package/ios/Utils/FWRTL/Classes/UICategories/FWRTLRemoteViewControllerAdaptor.m +86 -0
  57. package/ios/Utils/FWRTL/Classes/UICategories/FWRTLWhiteListManager.h +16 -0
  58. package/ios/Utils/FWRTL/Classes/UICategories/FWRTLWhiteListManager.m +55 -0
  59. package/ios/Utils/FWRTL/Classes/UICategories/UILabel+FWRTL.h +18 -0
  60. package/ios/Utils/FWRTL/Classes/UICategories/UILabel+FWRTL.m +39 -0
  61. package/ios/Utils/FWRTL/Classes/UICategories/UIView+FWRTL.h +54 -0
  62. package/ios/Utils/FWRTL/Classes/UICategories/UIView+FWRTL.m +141 -0
  63. package/ios/Utils/FWRTL/Classes/UICategories/UIWindow+FWRTL.h +16 -0
  64. package/ios/Utils/FWRTL/Classes/UICategories/UIWindow+FWRTL.m +20 -0
  65. package/ios/Utils/FWRTL/Classes/Utils/FWRTLDefinitions.h +52 -0
  66. package/ios/Utils/FWRTL/Classes/Utils/NSObject+FWRTLReloadBlock.h +19 -0
  67. package/ios/Utils/FWRTL/Classes/Utils/NSObject+FWRTLReloadBlock.m +49 -0
  68. package/ios/Utils/FWRTL/Classes/Utils/NSString+FWRTL.h +21 -0
  69. package/ios/Utils/FWRTL/Classes/Utils/NSString+FWRTL.m +38 -0
  70. package/ios/Utils/FWRTL/Classes/Utils/UIImage+FWRTL.h +18 -0
  71. package/ios/Utils/FWRTL/Classes/Utils/UIImage+FWRTL.m +43 -0
  72. package/ios/Utils/FWSwizzleLoader.m +6 -1
  73. package/ios/Utils/FWSwizzleLoader.swift +13 -0
  74. package/ios/Utils/FWSwizzleUtil.swift +17 -9
  75. package/ios/react_native_firework_sdk.h +1 -0
  76. package/ios/scripts/react_native_firework_sdk_pods.rb +31 -0
  77. package/lib/commonjs/FireworkSDK.js +21 -4
  78. package/lib/commonjs/FireworkSDK.js.map +1 -1
  79. package/lib/commonjs/VideoShopping.js +20 -37
  80. package/lib/commonjs/VideoShopping.js.map +1 -1
  81. package/lib/commonjs/components/StoryBlock.js +190 -125
  82. package/lib/commonjs/components/StoryBlock.js.map +1 -1
  83. package/lib/commonjs/components/VideoFeed.js +11 -1
  84. package/lib/commonjs/components/VideoFeed.js.map +1 -1
  85. package/lib/commonjs/index.js.map +1 -1
  86. package/lib/commonjs/models/FWEventName.js +2 -0
  87. package/lib/commonjs/models/FWEventName.js.map +1 -1
  88. package/lib/commonjs/models/ShoppingCTAResult.js +2 -0
  89. package/lib/commonjs/modules/FireworkSDKModule.js.map +1 -1
  90. package/lib/commonjs/modules/ShoppingModule.js.map +1 -1
  91. package/lib/module/FireworkSDK.js +21 -4
  92. package/lib/module/FireworkSDK.js.map +1 -1
  93. package/lib/module/VideoShopping.js +20 -39
  94. package/lib/module/VideoShopping.js.map +1 -1
  95. package/lib/module/components/StoryBlock.js +179 -131
  96. package/lib/module/components/StoryBlock.js.map +1 -1
  97. package/lib/module/components/VideoFeed.js +10 -1
  98. package/lib/module/components/VideoFeed.js.map +1 -1
  99. package/lib/module/index.js.map +1 -1
  100. package/lib/module/models/FWEventName.js +2 -0
  101. package/lib/module/models/FWEventName.js.map +1 -1
  102. package/lib/module/models/ShoppingCTAResult.js +2 -0
  103. package/lib/module/modules/FireworkSDKModule.js.map +1 -1
  104. package/lib/module/modules/ShoppingModule.js.map +1 -1
  105. package/lib/typescript/FireworkSDK.d.ts +7 -4
  106. package/lib/typescript/VideoShopping.d.ts +9 -11
  107. package/lib/typescript/components/StoryBlock.d.ts +19 -25
  108. package/lib/typescript/index.d.ts +6 -6
  109. package/lib/typescript/models/FWEventName.d.ts +2 -0
  110. package/lib/typescript/models/FWEvents.d.ts +14 -1
  111. package/lib/typescript/models/ProductInfoViewConfiguration.d.ts +36 -1
  112. package/lib/typescript/models/ShoppingCTAResult.d.ts +11 -0
  113. package/lib/typescript/modules/FireworkSDKModule.d.ts +1 -2
  114. package/lib/typescript/modules/ShoppingModule.d.ts +2 -1
  115. package/package.json +2 -2
  116. package/react-native-firework-sdk.podspec +26 -24
  117. package/src/FireworkSDK.ts +18 -5
  118. package/src/VideoShopping.ts +40 -53
  119. package/src/components/StoryBlock.tsx +199 -96
  120. package/src/components/VideoFeed.tsx +11 -0
  121. package/src/index.ts +15 -7
  122. package/src/models/FWEventName.ts +2 -0
  123. package/src/models/FWEvents.ts +14 -1
  124. package/src/models/ProductInfoViewConfiguration.ts +38 -1
  125. package/src/models/ShoppingCTAResult.ts +11 -0
  126. package/src/modules/FireworkSDKModule.ts +1 -2
  127. package/src/modules/ShoppingModule.ts +5 -5
  128. package/android/src/main/java/com/fireworksdk/bridge/constants/FWCommandConstant.kt +0 -6
  129. package/ios/Utils/UIView+ParentViewController.swift +0 -21
  130. package/lib/commonjs/models/AddToCartResult.js +0 -2
  131. package/lib/module/models/AddToCartResult.js +0 -2
  132. package/lib/typescript/models/AddToCartResult.d.ts +0 -10
  133. package/src/models/AddToCartResult.ts +0 -10
  134. /package/ios/Utils/{String+Color.swift → Extensions/String+Color.swift} +0 -0
  135. /package/ios/Utils/{UIView+Constraints.swift → Extensions/UIView+Constraints.swift} +0 -0
  136. /package/ios/Utils/{UIViewController+AttachChild.swift → Extensions/UIViewController+AttachChild.swift} +0 -0
  137. /package/lib/commonjs/models/{AddToCartResult.js.map → ShoppingCTAResult.js.map} +0 -0
  138. /package/lib/module/models/{AddToCartResult.js.map → ShoppingCTAResult.js.map} +0 -0
@@ -50,8 +50,8 @@ android {
50
50
  }
51
51
  }
52
52
 
53
- dataBinding {
54
- enabled = true
53
+ buildFeatures {
54
+ viewBinding = true
55
55
  }
56
56
 
57
57
  lintOptions {
@@ -151,7 +151,7 @@ dependencies {
151
151
  implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
152
152
 
153
153
 
154
- def fireworkSdkVersion = "6.0.0"
154
+ def fireworkSdkVersion = "6.1.1"
155
155
  implementation "com.firework:sdk:$fireworkSdkVersion"
156
156
  implementation "com.firework.external.imageloading:glide:$fireworkSdkVersion"
157
157
  implementation "com.firework.external.livestream:singleHostPlayer:$fireworkSdkVersion"
@@ -165,6 +165,8 @@ dependencies {
165
165
 
166
166
  // required to avoid crash on Android 12 API 31
167
167
  implementation 'androidx.work:work-runtime-ktx:2.7.0'
168
+
169
+ implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
168
170
  }
169
171
 
170
172
  configurations.all {
@@ -0,0 +1,129 @@
1
+ package com.fireworksdk.bridge.components.videofeed
2
+
3
+ import android.os.Bundle
4
+ import android.view.LayoutInflater
5
+ import android.view.View
6
+ import android.view.ViewGroup
7
+ import androidx.activity.OnBackPressedCallback
8
+ import androidx.fragment.app.Fragment
9
+ import com.firework.storyblock.FeedLoadListener
10
+ import com.firework.storyblock.FwStoryBlockView
11
+ import com.fireworksdk.bridge.databinding.FwBridgeStoryBlockBinding
12
+ import com.fireworksdk.bridge.models.FWVideoFeedPropsModel
13
+ import com.fireworksdk.bridge.utils.FWConfigUtil
14
+
15
+ class StoryBlockFragment : Fragment() {
16
+
17
+ private var _binding: FwBridgeStoryBlockBinding? = null
18
+ private val binding: FwBridgeStoryBlockBinding get() = _binding!!
19
+
20
+ private var feedLoadListener: FeedLoadListener? = null
21
+ private var fullScreenStateChangeListener: FwStoryBlockView.OnFullscreenStateChangeListener? = null
22
+ private var videoFeedPropsModel: FWVideoFeedPropsModel = FWVideoFeedPropsModel()
23
+
24
+ override fun onCreateView(
25
+ inflater: LayoutInflater,
26
+ container: ViewGroup?,
27
+ savedInstanceState: Bundle?,
28
+ ): View {
29
+ _binding = FwBridgeStoryBlockBinding.inflate(inflater)
30
+ return binding.root
31
+ }
32
+
33
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
34
+ super.onViewCreated(view, savedInstanceState)
35
+ afterUpdateTransaction()
36
+ // need to call it after init()
37
+ binding.storyBlock.setFeedLoadListener(feedLoadListener)
38
+ binding.storyBlock.setOnFullscreenStateChangeListener(fullScreenStateChangeListener)
39
+ // addBackPressedCallback()
40
+ // Handler(Looper.getMainLooper()).postDelayed({
41
+ // addBackPressedCallback()
42
+ // }, 10000)
43
+ }
44
+
45
+
46
+ override fun onDestroyView() {
47
+ // removeBackPressedCallback()
48
+ _binding = null
49
+ super.onDestroyView()
50
+ }
51
+
52
+ private fun afterUpdateTransaction() {
53
+ val viewOptionsBuilder = FWConfigUtil.generateViewOptionsBuilder(videoFeedPropsModel)
54
+ binding.storyBlock.init(
55
+ childFragmentManager,
56
+ requireActivity().lifecycle,
57
+ viewOptionsBuilder.build(),
58
+ binding.storyBlockContainer,
59
+ )
60
+ }
61
+
62
+ fun setProps(props: FWVideoFeedPropsModel?) {
63
+ props ?: return
64
+ videoFeedPropsModel = props
65
+ }
66
+
67
+ fun play() {
68
+ binding.storyBlock.play()
69
+ }
70
+
71
+ fun pause() {
72
+ binding.storyBlock.pause()
73
+ }
74
+
75
+ fun toggleFullScreen() {
76
+ binding.storyBlock.toggleFullscreen()
77
+ }
78
+
79
+ fun setFeedLoadListener(feedLoadListener: FeedLoadListener?) {
80
+ this.feedLoadListener = feedLoadListener
81
+ }
82
+
83
+ fun setOnFullScreenStateChangedListener(listener: FwStoryBlockView.OnFullscreenStateChangeListener?) {
84
+ this.fullScreenStateChangeListener = listener
85
+ }
86
+
87
+ fun isFullScreen(): Boolean {
88
+ return binding.storyBlock.isFullscreen()
89
+ }
90
+
91
+ private val mBackPressedCallback: OnBackPressedCallback by lazy {
92
+ object : OnBackPressedCallback(true) {
93
+ override fun handleOnBackPressed() {
94
+ if (binding.storyBlock.isFullscreen()) {
95
+ binding.storyBlock.toggleFullscreen()
96
+ } else {
97
+ isEnabled = false
98
+ requireActivity().onBackPressed()
99
+ }
100
+ }
101
+ }
102
+ }
103
+
104
+ private fun addBackPressedCallback() {
105
+ requireActivity()
106
+ .onBackPressedDispatcher
107
+ .addCallback(
108
+ viewLifecycleOwner,
109
+ mBackPressedCallback
110
+ )
111
+ }
112
+
113
+ private fun removeBackPressedCallback() {
114
+ mBackPressedCallback.remove()
115
+ }
116
+
117
+ override fun onPause() {
118
+ super.onPause()
119
+ }
120
+
121
+ override fun onResume() {
122
+ super.onResume()
123
+ }
124
+
125
+ override fun onDestroy() {
126
+ super.onDestroy()
127
+ }
128
+
129
+ }
@@ -0,0 +1,84 @@
1
+ package com.fireworksdk.bridge.components.videofeed
2
+
3
+ import android.content.Context
4
+ import android.util.AttributeSet
5
+ import android.widget.FrameLayout
6
+ import com.fireworksdk.bridge.models.*
7
+
8
+ class StoryBlockFrameLayout(
9
+ context: Context,
10
+ attrs: AttributeSet?
11
+ ) : FrameLayout(context, attrs) {
12
+
13
+ constructor(context: Context) : this(context, null)
14
+
15
+ private var videoFeedPropsModel: FWVideoFeedPropsModel = FWVideoFeedPropsModel()
16
+ private var storyBlockFragment by weakProperty<StoryBlockFragment?>()
17
+
18
+ fun setSource(source: String?) {
19
+ videoFeedPropsModel = videoFeedPropsModel.copy(
20
+ source = source
21
+ )
22
+ }
23
+
24
+ fun setChannel(channel: String?) {
25
+ videoFeedPropsModel = videoFeedPropsModel.copy(
26
+ channel = channel
27
+ )
28
+ }
29
+
30
+ fun setPlaylist(playlist: String?) {
31
+ videoFeedPropsModel = videoFeedPropsModel.copy(
32
+ playlist = playlist
33
+ )
34
+ }
35
+
36
+ fun setDynamicContentParameters(parametersMap: HashMap<String, List<String>>?) {
37
+ videoFeedPropsModel = videoFeedPropsModel.copy(
38
+ dynamicContentParameters = parametersMap
39
+ )
40
+ }
41
+
42
+ fun setEnablePictureInPicture(enablePictureInPicture: Boolean?) {
43
+ videoFeedPropsModel = videoFeedPropsModel.copy(
44
+ enablePictureInPicture = enablePictureInPicture
45
+ )
46
+ }
47
+
48
+ fun setVideoFeedConfig(config: FWVideoFeedConfigModel?) {
49
+ videoFeedPropsModel = videoFeedPropsModel.copy(
50
+ videoFeedConfiguration = config
51
+ )
52
+ }
53
+
54
+ fun setVideoPlayerConfig(config: FWVideoPlayerConfigModel?) {
55
+ videoFeedPropsModel = videoFeedPropsModel.copy(
56
+ videoPlayerConfiguration = config
57
+ )
58
+ }
59
+
60
+ fun setFragment(fragment: StoryBlockFragment) {
61
+ this.storyBlockFragment = fragment
62
+ fragment.setProps(videoFeedPropsModel)
63
+ }
64
+
65
+ fun getFragment(): StoryBlockFragment? {
66
+ return storyBlockFragment
67
+ }
68
+
69
+ fun play() {
70
+ storyBlockFragment?.play()
71
+ }
72
+
73
+ fun pause() {
74
+ storyBlockFragment?.pause()
75
+ }
76
+
77
+ fun toggleFullScreen() {
78
+ storyBlockFragment?.toggleFullScreen()
79
+ }
80
+
81
+ fun destroy() {
82
+ storyBlockFragment = null
83
+ }
84
+ }
@@ -31,7 +31,7 @@ enum class FWVideoPlaybackSubEventName(val rawValue: String) {
31
31
  }
32
32
 
33
33
  enum class FWVideoShoppingEventName(val rawValue: String) {
34
- AddToCart("fw:shopping:add-to-cart"),
34
+ CtaButtonClick("fw:shopping:cta-button-click"),
35
35
  ClickCartIcon("fw:shopping:click-cart-icon"),
36
36
  UpdateProductDetails("fw:shopping:update-product-details"),
37
37
  WillDisplayProduct("fw:shopping:will-display-product"),
@@ -47,4 +47,5 @@ enum class FWLiveStreamEventName(val rawValue: String) {
47
47
 
48
48
  enum class FWStoryBlockEventName(val rawValue: String) {
49
49
  StoryBlockLoadFinished("onStoryBlockLoadFinished"),
50
+ StoryBlockFullScreenStateChanged("onStoryBlockFullScreenStateChanged"),
50
51
  }
@@ -5,13 +5,26 @@ import kotlinx.android.parcel.Parcelize
5
5
 
6
6
  @Parcelize
7
7
  data class FWProductInfoViewConfiguration(
8
- var addToCartButton: AddToCartButtonStyle? = null,
8
+ val ctaButton: CtaButtonConfiguration? = null,
9
+ val linkButton: LinkButtonConfiguration? = null,
9
10
  ) : Parcelable {
10
11
 
11
12
  @Parcelize
12
- data class AddToCartButtonStyle(
13
- var backgroundColor: String? = null,
14
- var textColor: String? = null,
15
- var fontSize: Int? = null,
13
+ data class CtaButtonConfiguration(
14
+ val text: TextValue? = null,
15
+ val backgroundColor: String? = null,
16
+ val textColor: String? = null,
17
+ val fontSize: Int? = null,
18
+ ) : Parcelable {
19
+
20
+ enum class TextValue(val rawValue: String) {
21
+ AddToCart("addToCart"),
22
+ ShopNow("shopNow"),
23
+ }
24
+ }
25
+
26
+ @Parcelize
27
+ data class LinkButtonConfiguration(
28
+ val isHidden: Boolean? = null,
16
29
  ) : Parcelable
17
30
  }
@@ -4,32 +4,56 @@ import org.json.JSONObject
4
4
 
5
5
  object FWProductInfoViewConfigurationDeserializer {
6
6
 
7
- private const val ADD_TO_CART_BUTTON_STYLE = "addToCartButton"
7
+ private const val CTA_BUTTON = "ctaButton"
8
+ private const val LINK_BUTTON = "linkButton"
8
9
 
10
+ private const val TEXT_KEY = "text"
9
11
  private const val BACKGROUND_COLOR_KEY = "backgroundColor"
10
12
  private const val TEXT_COLOR_KEY = "textColor"
11
13
  private const val FONT_SIZE_KEY = "fontSize"
12
14
 
15
+ private const val IS_HIDDEN_KEY = "isHidden"
16
+
13
17
  fun deserialize(responseJson: JSONObject?): FWProductInfoViewConfiguration? {
14
18
  responseJson ?: return null
15
19
 
16
- val addToCartButton = deserializeAddToCartButtonStyle(responseJson.optJSONObject(ADD_TO_CART_BUTTON_STYLE))
20
+ val ctaButtonConfiguration = deserializeCtaButtonConfiguration(responseJson.optJSONObject(CTA_BUTTON))
21
+ val linkButtonConfiguration = deserializeLinkButtonConfiguration(responseJson.optJSONObject(LINK_BUTTON))
17
22
 
18
23
  return FWProductInfoViewConfiguration(
19
- addToCartButton = addToCartButton
24
+ ctaButton = ctaButtonConfiguration,
25
+ linkButton = linkButtonConfiguration,
20
26
  )
21
27
  }
22
28
 
23
- private fun deserializeAddToCartButtonStyle(addToCartButtonStyleJson: JSONObject?): FWProductInfoViewConfiguration.AddToCartButtonStyle {
24
- val backgroundColor = addToCartButtonStyleJson?.optString(BACKGROUND_COLOR_KEY)
25
- val textColor = addToCartButtonStyleJson?.optString(TEXT_COLOR_KEY)
26
- val fontSize = addToCartButtonStyleJson?.optInt(FONT_SIZE_KEY)
27
-
28
- return FWProductInfoViewConfiguration.AddToCartButtonStyle(
29
+ private fun deserializeCtaButtonConfiguration(ctaButtonConfiguration: JSONObject?): FWProductInfoViewConfiguration.CtaButtonConfiguration {
30
+ val textString = ctaButtonConfiguration?.optString(TEXT_KEY)
31
+ val backgroundColor = ctaButtonConfiguration?.optString(BACKGROUND_COLOR_KEY)
32
+ val textColor = ctaButtonConfiguration?.optString(TEXT_COLOR_KEY)
33
+ val fontSize = ctaButtonConfiguration?.optInt(FONT_SIZE_KEY)
34
+
35
+ var text = FWProductInfoViewConfiguration.CtaButtonConfiguration.TextValue.AddToCart
36
+ when (textString) {
37
+ FWProductInfoViewConfiguration.CtaButtonConfiguration.TextValue.ShopNow.rawValue -> {
38
+ text = FWProductInfoViewConfiguration.CtaButtonConfiguration.TextValue.ShopNow
39
+ }
40
+ else -> {}
41
+ }
42
+
43
+ return FWProductInfoViewConfiguration.CtaButtonConfiguration(
44
+ text = text,
29
45
  backgroundColor = backgroundColor,
30
46
  textColor = textColor,
31
47
  fontSize = fontSize,
32
48
  )
33
49
  }
34
50
 
51
+ private fun deserializeLinkButtonConfiguration(addToCartButtonStyleJson: JSONObject?): FWProductInfoViewConfiguration.LinkButtonConfiguration {
52
+ val isHidden = addToCartButtonStyleJson?.optBoolean(IS_HIDDEN_KEY)
53
+
54
+ return FWProductInfoViewConfiguration.LinkButtonConfiguration(
55
+ isHidden = isHidden,
56
+ )
57
+ }
58
+
35
59
  }
@@ -0,0 +1,17 @@
1
+ package com.fireworksdk.bridge.models
2
+
3
+ import android.os.Parcelable
4
+ import kotlinx.android.parcel.Parcelize
5
+
6
+ @Parcelize
7
+ data class FWShoppingCtaResult(
8
+ val res: Res? = null,
9
+ val tips: String? = null,
10
+ ) : Parcelable {
11
+
12
+ enum class Res(val rawValue: String) {
13
+ Success("success"),
14
+ Fail("fail"),
15
+ Loading("loading"),
16
+ }
17
+ }
@@ -0,0 +1,33 @@
1
+ package com.fireworksdk.bridge.models
2
+
3
+ import org.json.JSONObject
4
+
5
+ object FWShoppingCtaResultDeserializer {
6
+
7
+ private const val RES_KEY = "res"
8
+ private const val TIPS_KEY = "tips"
9
+
10
+
11
+ fun deserialize(responseJson: JSONObject?): FWShoppingCtaResult? {
12
+ responseJson?: return null
13
+
14
+ val resString = responseJson.optString(RES_KEY)
15
+ val tips = responseJson.optString(TIPS_KEY)
16
+
17
+ var res = FWShoppingCtaResult.Res.Success
18
+ when (resString) {
19
+ FWShoppingCtaResult.Res.Fail.rawValue -> {
20
+ res = FWShoppingCtaResult.Res.Fail
21
+ }
22
+ FWShoppingCtaResult.Res.Loading.rawValue -> {
23
+ res = FWShoppingCtaResult.Res.Loading
24
+ }
25
+ else -> {}
26
+ }
27
+
28
+ return FWShoppingCtaResult(
29
+ res = res,
30
+ tips = tips,
31
+ )
32
+ }
33
+ }
@@ -3,10 +3,7 @@ package com.fireworksdk.bridge.reactnative.manager
3
3
  import android.view.Choreographer
4
4
  import android.view.View
5
5
  import android.view.ViewGroup
6
- import android.widget.FrameLayout
7
- import androidx.annotation.Nullable
8
6
  import androidx.appcompat.app.AppCompatActivity
9
- import com.facebook.react.bridge.Dynamic
10
7
  import com.facebook.react.bridge.ReactApplicationContext
11
8
  import com.facebook.react.bridge.ReadableArray
12
9
  import com.facebook.react.bridge.ReadableMap
@@ -14,78 +11,102 @@ import com.facebook.react.common.MapBuilder
14
11
  import com.facebook.react.uimanager.ThemedReactContext
15
12
  import com.facebook.react.uimanager.ViewGroupManager
16
13
  import com.facebook.react.uimanager.annotations.ReactProp
17
- import com.facebook.react.uimanager.annotations.ReactPropGroup
18
- import com.fireworksdk.bridge.models.FWStoryBlockEventName
19
- import com.fireworksdk.bridge.models.FWVideoFeedPropsModel
14
+ import com.firework.storyblock.FeedLoadState
15
+ import com.fireworksdk.bridge.components.videofeed.StoryBlockFragment
16
+ import com.fireworksdk.bridge.components.videofeed.StoryBlockFrameLayout
17
+ import com.fireworksdk.bridge.models.*
18
+ import com.fireworksdk.bridge.reactnative.utils.FWEventUtils
19
+ import com.fireworksdk.bridge.utils.FWGlobalDataUtil
20
20
  import com.fireworksdk.bridge.utils.FWLogUtils
21
+ import org.json.JSONObject
21
22
 
22
23
 
23
24
  class FWStoryBlockManager(
24
25
  private val reactContext: ReactApplicationContext
25
- ) : ViewGroupManager<FrameLayout>() {
26
-
27
- private val videoFeedPropsModel: FWVideoFeedPropsModel by lazy { FWVideoFeedPropsModel() }
28
-
29
- private var viewId: Int? = null
26
+ ) : ViewGroupManager<StoryBlockFrameLayout>() {
30
27
 
31
28
  override fun getName(): String {
32
29
  return REACT_CLASS
33
30
  }
34
31
 
35
- override fun createViewInstance(reactContext: ThemedReactContext): FrameLayout {
36
- return FrameLayout(reactContext)
32
+ override fun createViewInstance(reactContext: ThemedReactContext): StoryBlockFrameLayout {
33
+ return StoryBlockFrameLayout(reactContext)
37
34
  }
38
35
 
39
- override fun onAfterUpdateTransaction(view: FrameLayout) {
36
+ override fun onAfterUpdateTransaction(view: StoryBlockFrameLayout) {
40
37
  super.onAfterUpdateTransaction(view)
38
+ // after createFragment
41
39
  FWLogUtils.d { "FWStoryBlockManager onAfterUpdateTransaction" }
42
40
  }
43
41
 
44
- @Nullable
45
- override fun getCommandsMap(): Map<String, Int>? {
46
- return MapBuilder.of("create", COMMAND_CREATE)
42
+ override fun getCommandsMap(): Map<String, Int> {
43
+ val map: MutableMap<String, Int> = HashMap()
44
+ map["create"] = COMMAND_CREATE
45
+ map["play"] = COMMAND_PLAY
46
+ map["pause"] = COMMAND_PAUSE
47
+ map["toggle_full_screen"] = COMMAND_TOGGLE_FULL_SCREEN
48
+ return map
47
49
  }
48
50
 
49
51
  override fun receiveCommand(
50
- root: FrameLayout,
52
+ root: StoryBlockFrameLayout,
51
53
  commandId: String?,
52
54
  args: ReadableArray?
53
55
  ) {
54
56
  super.receiveCommand(root, commandId, args)
55
- val reactNativeViewId = requireNotNull(args).getInt(0)
56
-
57
- FWLogUtils.d { "FWStoryBlockManager receiveCommand commandId: $commandId, reactNativeViewId: $reactNativeViewId" }
57
+ FWLogUtils.d { "FWStoryBlockManager receiveCommand commandId: $commandId, root.isAttachedToWindow: ${root.isAttachedToWindow}" }
58
58
  when (commandId?.toInt()) {
59
- COMMAND_CREATE -> createFragment(root, reactNativeViewId)
59
+ COMMAND_CREATE -> {
60
+ val reactNativeViewId = requireNotNull(args).getInt(0)
61
+ createFragment(root, reactNativeViewId)
62
+ }
63
+ COMMAND_PLAY -> root.play()
64
+ COMMAND_PAUSE -> root.pause()
65
+ COMMAND_TOGGLE_FULL_SCREEN -> root.toggleFullScreen()
60
66
  else -> {}
61
67
  }
62
68
  }
63
69
 
64
70
  @ReactProp(name = "source")
65
- fun setSource(view: FrameLayout, source: String?) {
66
- // videoFeedPropsModel.source = source
71
+ fun setSource(view: StoryBlockFrameLayout, source: String?) {
72
+ view.setSource(source)
67
73
  }
68
74
 
69
75
  @ReactProp(name = "channel")
70
- fun setChannel(view: FrameLayout, channel: String?) {
71
- // videoFeedPropsModel.channel = channel
76
+ fun setChannel(view: StoryBlockFrameLayout, channel: String?) {
77
+ view.setChannel(channel)
72
78
  }
73
79
 
74
80
  @ReactProp(name = "playlist")
75
- fun setPlaylist(view: FrameLayout, playlist: String?) {
76
- // videoFeedPropsModel.playlist = playlist
81
+ fun setPlaylist(view: StoryBlockFrameLayout, playlist: String?) {
82
+ view.setPlaylist(playlist)
77
83
  }
78
84
 
79
85
  @ReactProp(name = "dynamicContentParameters")
80
- fun setDynamicContentParameters(view: FrameLayout, parameters: ReadableMap?) {
86
+ fun setDynamicContentParameters(view: StoryBlockFrameLayout, parameters: ReadableMap?) {
81
87
  val parametersMap = parameters?.toHashMap() as? HashMap<String, List<String>>
82
- // videoFeedPropsModel.dynamicContentParameters = parametersMap
88
+ view.setDynamicContentParameters(parametersMap)
89
+ }
90
+
91
+ @ReactProp(name = "enablePictureInPicture")
92
+ fun setEnablePictureInPicture(view: StoryBlockFrameLayout, enablePictureInPicture: Boolean?) {
93
+ view.setEnablePictureInPicture(enablePictureInPicture)
94
+ }
95
+
96
+ @ReactProp(name = "videoFeedConfiguration")
97
+ fun setVideoFeedConfig(view: StoryBlockFrameLayout, config: ReadableMap?) {
98
+ val configMap = config?.toHashMap() ?: hashMapOf()
99
+ val jsonObject = JSONObject(configMap)
100
+ val videoFeedConfigModel = FWVideoFeedConfigModelDeserializer.deserialize(jsonObject)
101
+ view.setVideoFeedConfig(videoFeedConfigModel)
83
102
  }
84
103
 
85
- @ReactPropGroup(names = ["width", "height"], customType = "Style")
86
- fun setStyle(view: FrameLayout, index: Int, value: Dynamic) {
87
- // if (index == 0) propWidth = value
88
- // if (index == 1) propHeight = value
104
+ @ReactProp(name = "videoPlayerConfiguration")
105
+ fun setVideoPlayerConfig(view: StoryBlockFrameLayout, config: ReadableMap?) {
106
+ val configMap = config?.toHashMap() ?: hashMapOf()
107
+ val jsonObject = JSONObject(configMap)
108
+ val videoPlayerConfigModel = FWVideoPlayerConfigModelDeserializer.deserialize(jsonObject)
109
+ view.setVideoPlayerConfig(videoPlayerConfigModel)
89
110
  }
90
111
 
91
112
  private fun setupLayout(view: View) {
@@ -100,46 +121,106 @@ class FWStoryBlockManager(
100
121
  })
101
122
  }
102
123
 
103
- /**
104
- * Layout all children properly
105
- */
106
124
  private fun manuallyLayoutChildren(view: View) {
107
125
  view.measure(
108
126
  View.MeasureSpec.makeMeasureSpec(view.measuredWidth, View.MeasureSpec.EXACTLY),
109
127
  View.MeasureSpec.makeMeasureSpec(view.measuredHeight, View.MeasureSpec.EXACTLY)
110
128
  )
111
-
112
129
  view.layout(view.left, view.top, view.right, view.bottom)
113
130
  }
114
131
 
132
+ private fun addStoryBlockListener(fragment: StoryBlockFragment, reactNativeViewId: Int) {
133
+ fragment.setFeedLoadListener { feedLoadState ->
134
+ when (feedLoadState) {
135
+ FeedLoadState.FeedLoaded -> {
136
+ FWEventUtils.receiveStoryBlockLoadFinishedSuccessEvent(reactContext, reactNativeViewId)
137
+ }
138
+ FeedLoadState.FeedLoadFailed -> {
139
+ FWEventUtils.receiveStoryBlockLoadFinishedFailedEvent(
140
+ reactContext,
141
+ reactNativeViewId,
142
+ "FeedLoadFailed",
143
+ "FeedLoadFailed"
144
+ )
145
+ }
146
+ else -> {}
147
+ }
148
+ }
115
149
 
116
- private fun createFragment(root: FrameLayout, reactNativeViewId: Int?) {
150
+ fragment.setOnFullScreenStateChangedListener { isFullScreen ->
151
+ if (isFullScreen) {
152
+ FWGlobalDataUtil.storyBlockFragment = fragment
153
+ }
154
+ FWEventUtils.receiveStoryBlockFullScreenStateChangedEvent(
155
+ reactContext,
156
+ reactNativeViewId,
157
+ isFullScreen
158
+ )
159
+ }
160
+ }
161
+
162
+
163
+ private fun createFragment(root: StoryBlockFrameLayout, reactNativeViewId: Int?) {
117
164
  FWLogUtils.d { "FWStoryBlockManager createFragment, reactNativeViewId = $reactNativeViewId" }
118
165
  reactNativeViewId ?: return
119
166
  val activity = (reactContext.currentActivity as AppCompatActivity?) ?: return
120
167
 
121
- viewId = reactNativeViewId
122
-
123
168
  val parentView = root.findViewById(reactNativeViewId) as ViewGroup
124
169
  // parentView.setBackgroundColor(Color.YELLOW)
125
170
  setupLayout(parentView)
171
+
172
+ val fragment = StoryBlockFragment()
173
+ root.setFragment(fragment)
174
+ addStoryBlockListener(fragment, reactNativeViewId)
175
+
176
+ if (parentView.isAttachedToWindow) {
177
+ if (!fragment.isAdded) {
178
+ activity.supportFragmentManager
179
+ .beginTransaction()
180
+ .replace(reactNativeViewId, fragment, reactNativeViewId.toString())
181
+ .commit()
182
+ }
183
+ } else {
184
+ parentView.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
185
+ override fun onViewAttachedToWindow(v: View?) {
186
+ FWLogUtils.d { "FWStoryBlockManager createFragment doOnAttach" }
187
+ if (!fragment.isAdded) {
188
+ activity.supportFragmentManager
189
+ .beginTransaction()
190
+ .replace(reactNativeViewId, fragment, reactNativeViewId.toString())
191
+ .commit()
192
+ }
193
+ }
194
+
195
+ override fun onViewDetachedFromWindow(v: View?) {
196
+ }
197
+ })
198
+ }
126
199
  }
127
200
 
128
- override fun onDropViewInstance(view: FrameLayout) {
201
+ override fun onDropViewInstance(view: StoryBlockFrameLayout) {
202
+ val activity = reactContext.currentActivity as AppCompatActivity
203
+ val fragment = view.getFragment()
204
+ fragment?.let {
205
+ activity.supportFragmentManager.beginTransaction().remove(it).commit()
206
+ }
207
+ view.destroy()
129
208
  super.onDropViewInstance(view)
130
209
  }
131
210
 
132
211
  override fun getExportedCustomBubblingEventTypeConstants(): MutableMap<String, Any>? {
133
212
  return MapBuilder.builder<String, Any>()
134
213
  .put(FWStoryBlockEventName.StoryBlockLoadFinished.rawValue, MapBuilder.of("phasedRegistrationNames", MapBuilder.of("bubbled", FWStoryBlockEventName.StoryBlockLoadFinished.rawValue)))
214
+ .put(FWStoryBlockEventName.StoryBlockFullScreenStateChanged.rawValue, MapBuilder.of("phasedRegistrationNames", MapBuilder.of("bubbled", FWStoryBlockEventName.StoryBlockFullScreenStateChanged.rawValue)))
135
215
  .build()
136
216
  }
137
217
 
138
218
  companion object {
139
219
  private const val REACT_CLASS = "FWStoryBlock"
140
- const val FW_STORY_BLOCK_INIT_ACTION = "FW_STORY_BLOCK_INIT_ACTION"
141
- const val FW_STORY_BLOCK_INIT_INTENT = "FW_STORY_BLOCK_INIT_INTENT"
142
- private const val COMMAND_CREATE = 1
220
+ private const val COMMAND_CREATE = 1000000
221
+ private const val COMMAND_PLAY = 1000001
222
+ private const val COMMAND_PAUSE = 1000002
223
+ private const val COMMAND_TOGGLE_FULL_SCREEN = 1000003
143
224
  }
144
225
 
145
226
  }