react-native-firework-sdk 2.4.2 → 2.5.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 (102) hide show
  1. package/FireworkVideoUI.xcframework/Info.plist +5 -5
  2. package/FireworkVideoUI.xcframework/ios-arm64/FireworkVideoUI.framework/FireworkVideoUI +0 -0
  3. package/FireworkVideoUI.xcframework/ios-arm64/FireworkVideoUI.framework/Info.plist +0 -0
  4. package/FireworkVideoUI.xcframework/ios-arm64_x86_64-simulator/FireworkVideoUI.framework/FireworkVideoUI +0 -0
  5. package/FireworkVideoUI.xcframework/ios-arm64_x86_64-simulator/FireworkVideoUI.framework/Info.plist +0 -0
  6. package/FireworkVideoUI.xcframework/ios-arm64_x86_64-simulator/FireworkVideoUI.framework/_CodeSignature/CodeResources +1 -1
  7. package/android/src/main/java/com/fireworksdk/bridge/models/FWProductInfoViewConfiguration.kt +8 -0
  8. package/android/src/main/java/com/fireworksdk/bridge/models/FWProductInfoViewConfigurationDeserializer.kt +25 -5
  9. package/android/src/main/java/com/fireworksdk/bridge/models/FWProductInfoViewConfigurationSerializer.kt +17 -4
  10. package/android/src/main/java/com/fireworksdk/bridge/models/FWVideoFeedPropsModel.kt +1 -0
  11. package/android/src/main/java/com/fireworksdk/bridge/models/FWVideoFeedPropsModelDeserializer.kt +4 -0
  12. package/android/src/main/java/com/fireworksdk/bridge/models/FWVideoFeedPropsModelSerializer.kt +2 -0
  13. package/android/src/main/java/com/fireworksdk/bridge/models/enums/FWProductCardCtaButtonTextValue.kt +18 -0
  14. package/android/src/main/java/com/fireworksdk/bridge/models/enums/FWProductCardTheme.kt +18 -0
  15. package/android/src/main/java/com/fireworksdk/bridge/models/enums/FWVideoFeedSource.kt +2 -1
  16. package/android/src/main/java/com/fireworksdk/bridge/reactnative/manager/FWStoryBlockManager.kt +7 -0
  17. package/android/src/main/java/com/fireworksdk/bridge/reactnative/manager/FWVideoFeedManager.kt +7 -0
  18. package/android/src/main/java/com/fireworksdk/bridge/reactnative/module/FWVideoShoppingModule.kt +1 -1
  19. package/android/src/main/java/com/fireworksdk/bridge/reactnative/module/FireworkSDKModule.kt +2 -0
  20. package/android/src/main/java/com/fireworksdk/bridge/utils/FWColorUtil.kt +65 -0
  21. package/android/src/main/java/com/fireworksdk/bridge/utils/FWConfigUtil.kt +7 -8
  22. package/ios/Components/StoryBlock.swift +4 -1
  23. package/ios/Components/StoryBlockManager.m +1 -0
  24. package/ios/Components/VideoFeed.swift +39 -10
  25. package/ios/Components/VideoFeedManager.m +1 -0
  26. package/ios/Components/VideoPlayerConfiguration.swift +10 -0
  27. package/ios/FireworkSdk.xcodeproj/project.pbxproj +4 -0
  28. package/ios/FireworkVideoUI/FireworkVideoUI/Sources/AppLanguage/Extensions/UIKit/UIView+AppLanguage.swift +6 -6
  29. package/ios/FireworkVideoUI/Podfile +1 -1
  30. package/ios/FireworkVideoUI/Podfile.lock +4 -4
  31. package/ios/Models/Common/ButtonInfo.swift +37 -0
  32. package/ios/Models/NativeToRN/FireworkEventName.swift +1 -0
  33. package/ios/Models/NativeToRN/FireworkSDK+Json.swift +11 -1
  34. package/ios/Models/RNToNative/RCTConvert+FireworkSDKModule.swift +18 -0
  35. package/ios/Models/RNToNative/RCTConvert+StoryBlock.swift +2 -1
  36. package/ios/Models/RNToNative/RCTConvert+VideoFeed.swift +2 -1
  37. package/ios/Modules/FireworkSDKModule/FireworkSDKModule.swift +4 -4
  38. package/ios/Modules/Shopping/ProductInfoViewConfiguration.swift +16 -0
  39. package/ios/Modules/Shopping/ShoppingModule.m +1 -0
  40. package/ios/Modules/Shopping/ShoppingModule.swift +58 -17
  41. package/ios/Utils/Extensions/String+Color.swift +53 -27
  42. package/lib/commonjs/FWNavigator.js +0 -6
  43. package/lib/commonjs/FWNavigator.js.map +1 -1
  44. package/lib/commonjs/VideoShopping.js +32 -0
  45. package/lib/commonjs/VideoShopping.js.map +1 -1
  46. package/lib/commonjs/components/StoryBlock.js +2 -0
  47. package/lib/commonjs/components/StoryBlock.js.map +1 -1
  48. package/lib/commonjs/components/VideoFeed.js +2 -0
  49. package/lib/commonjs/components/VideoFeed.js.map +1 -1
  50. package/lib/commonjs/index.js.map +1 -1
  51. package/lib/commonjs/models/ButtonInfo.js +2 -0
  52. package/lib/commonjs/models/ButtonInfo.js.map +1 -0
  53. package/lib/commonjs/models/FWEventName.js +1 -0
  54. package/lib/commonjs/models/FWEventName.js.map +1 -1
  55. package/lib/commonjs/models/VideoPlayerButtonConfiguration.js +2 -0
  56. package/lib/commonjs/models/VideoPlayerButtonConfiguration.js.map +1 -0
  57. package/lib/commonjs/modules/ShoppingModule.js.map +1 -1
  58. package/lib/module/FWNavigator.js +0 -2
  59. package/lib/module/FWNavigator.js.map +1 -1
  60. package/lib/module/VideoShopping.js +31 -0
  61. package/lib/module/VideoShopping.js.map +1 -1
  62. package/lib/module/components/StoryBlock.js +2 -0
  63. package/lib/module/components/StoryBlock.js.map +1 -1
  64. package/lib/module/components/VideoFeed.js +2 -0
  65. package/lib/module/components/VideoFeed.js.map +1 -1
  66. package/lib/module/index.js.map +1 -1
  67. package/lib/module/models/ButtonInfo.js +2 -0
  68. package/lib/module/models/ButtonInfo.js.map +1 -0
  69. package/lib/module/models/FWEventName.js +1 -0
  70. package/lib/module/models/FWEventName.js.map +1 -1
  71. package/lib/module/models/VideoPlayerButtonConfiguration.js +2 -0
  72. package/lib/module/models/VideoPlayerButtonConfiguration.js.map +1 -0
  73. package/lib/module/modules/ShoppingModule.js.map +1 -1
  74. package/lib/typescript/VideoShopping.d.ts +13 -1
  75. package/lib/typescript/components/StoryBlock.d.ts +4 -0
  76. package/lib/typescript/components/VideoFeed.d.ts +4 -0
  77. package/lib/typescript/index.d.ts +3 -3
  78. package/lib/typescript/models/ButtonInfo.d.ts +18 -0
  79. package/lib/typescript/models/FWEventName.d.ts +2 -1
  80. package/lib/typescript/models/FWEvents.d.ts +18 -0
  81. package/lib/typescript/models/ProductInfoViewConfiguration.d.ts +16 -0
  82. package/lib/typescript/models/StoryBlockSource.d.ts +1 -1
  83. package/lib/typescript/models/VideoFeedSource.d.ts +1 -1
  84. package/lib/typescript/models/VideoPlayerButtonConfiguration.d.ts +39 -0
  85. package/lib/typescript/models/VideoPlayerConfiguration.d.ts +6 -0
  86. package/lib/typescript/modules/ShoppingModule.d.ts +1 -0
  87. package/package.json +1 -1
  88. package/react-native-firework-sdk.podspec +1 -1
  89. package/src/FWNavigator.ts +0 -2
  90. package/src/VideoShopping.ts +40 -0
  91. package/src/components/StoryBlock.tsx +6 -0
  92. package/src/components/VideoFeed.tsx +6 -0
  93. package/src/index.ts +8 -0
  94. package/src/models/ButtonInfo.ts +18 -0
  95. package/src/models/FWEventName.ts +1 -0
  96. package/src/models/FWEvents.ts +19 -0
  97. package/src/models/ProductInfoViewConfiguration.ts +19 -0
  98. package/src/models/StoryBlockSource.ts +2 -1
  99. package/src/models/VideoFeedSource.ts +2 -1
  100. package/src/models/VideoPlayerButtonConfiguration.ts +40 -0
  101. package/src/models/VideoPlayerConfiguration.ts +6 -0
  102. package/src/modules/ShoppingModule.ts +1 -0
@@ -71,6 +71,7 @@
71
71
  8988736C2A0A8E7E0089CD1C /* VideoPlayerConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 898873442A0A8E7E0089CD1C /* VideoPlayerConfiguration.swift */; };
72
72
  8988736D2A0A8E7E0089CD1C /* VideoFeedManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 898873452A0A8E7E0089CD1C /* VideoFeedManager.m */; };
73
73
  89C4D6A62A494EB800EFB74A /* StoryBlockConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89C4D6A52A494EB800EFB74A /* StoryBlockConfiguration.swift */; };
74
+ 89C6791A2A736C80000C1A71 /* ButtonInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89C679192A736C80000C1A71 /* ButtonInfo.swift */; };
74
75
  89D6BBF929ACE2DC00C8AA2A /* (null) in Sources */ = {isa = PBXBuildFile; };
75
76
  89DF27DD28A53A77003F3CCB /* (null) in Sources */ = {isa = PBXBuildFile; };
76
77
  89DF27DE28A53A77003F3CCB /* (null) in Sources */ = {isa = PBXBuildFile; };
@@ -133,6 +134,7 @@
133
134
  898873442A0A8E7E0089CD1C /* VideoPlayerConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VideoPlayerConfiguration.swift; sourceTree = "<group>"; };
134
135
  898873452A0A8E7E0089CD1C /* VideoFeedManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VideoFeedManager.m; sourceTree = "<group>"; };
135
136
  89C4D6A52A494EB800EFB74A /* StoryBlockConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoryBlockConfiguration.swift; sourceTree = "<group>"; };
137
+ 89C679192A736C80000C1A71 /* ButtonInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonInfo.swift; sourceTree = "<group>"; };
136
138
  /* End PBXFileReference section */
137
139
 
138
140
  /* Begin PBXFrameworksBuildPhase section */
@@ -248,6 +250,7 @@
248
250
  isa = PBXGroup;
249
251
  children = (
250
252
  898873332A0A8E7E0089CD1C /* FontInfo.swift */,
253
+ 89C679192A736C80000C1A71 /* ButtonInfo.swift */,
251
254
  );
252
255
  path = Common;
253
256
  sourceTree = "<group>";
@@ -406,6 +409,7 @@
406
409
  8988736B2A0A8E7E0089CD1C /* VideoFeed.swift in Sources */,
407
410
  891F4AF62A67E12800A9E8DA /* FWRNContainerViewController.swift in Sources */,
408
411
  898873522A0A8E7E0089CD1C /* ShoppingCTAResult.swift in Sources */,
412
+ 89C6791A2A736C80000C1A71 /* ButtonInfo.swift in Sources */,
409
413
  8975239D2817DEF80070EBB6 /* (null) in Sources */,
410
414
  898873562A0A8E7E0089CD1C /* SDKInitOptions.swift in Sources */,
411
415
  89DF27DE28A53A77003F3CCB /* (null) in Sources */,
@@ -30,7 +30,7 @@ extension UIView {
30
30
  @objc func fw_init(frame: CGRect) -> UIView {
31
31
  let view = self.fw_init(frame: frame)
32
32
  updateViewTypeIfNeeded(view)
33
-
33
+
34
34
  return view
35
35
  }
36
36
 
@@ -38,21 +38,21 @@ extension UIView {
38
38
  self.fw_viewAwakeFromNib()
39
39
  updateViewTypeIfNeeded(self)
40
40
  }
41
-
41
+
42
42
  @objc func fw_semanticContentAttribute() -> UISemanticContentAttribute {
43
43
  if self.isIOSSDKView, AppLanguageManager.shared.shouldHorizontalFlip {
44
44
  return .forceLeftToRight
45
45
  }
46
-
46
+
47
47
  return fw_semanticContentAttribute()
48
48
  }
49
-
49
+
50
50
  private func updateViewTypeIfNeeded(_ view: UIView) {
51
51
  if view is FireworkPlayerView, AppLanguageManager.shared.shouldHorizontalFlip {
52
52
  view.viewType = .normal
53
53
  }
54
-
55
- DispatchQueue.main.async {
54
+
55
+ DispatchQueue.main.async {
56
56
  if view.layer.sublayers?.first(where: { layer in
57
57
  layer is AVPlayerLayer
58
58
  }) != nil, AppLanguageManager.shared.shouldHorizontalFlip {
@@ -6,7 +6,7 @@ target 'FireworkVideoUI' do
6
6
  use_frameworks!
7
7
 
8
8
  # Pods for FireworkVideoUI
9
- pod 'FireworkVideo', '1.11.0'
9
+ pod 'FireworkVideo', '1.12.0'
10
10
 
11
11
  target 'FireworkVideoUITests' do
12
12
  # Pods for testing
@@ -1,16 +1,16 @@
1
1
  PODS:
2
- - FireworkVideo (1.11.0)
2
+ - FireworkVideo (1.12.0)
3
3
 
4
4
  DEPENDENCIES:
5
- - FireworkVideo (= 1.11.0)
5
+ - FireworkVideo (= 1.12.0)
6
6
 
7
7
  SPEC REPOS:
8
8
  trunk:
9
9
  - FireworkVideo
10
10
 
11
11
  SPEC CHECKSUMS:
12
- FireworkVideo: d37d9b94d8bc8069c42a3f06b4b9da97ea3b7e60
12
+ FireworkVideo: 8f7667ac08b5bb5a0bc09656f4cb6b782dcddccd
13
13
 
14
- PODFILE CHECKSUM: 19727e7fd482efd91e8a58ce9db72b1ded933875
14
+ PODFILE CHECKSUM: 45fcc23fac9e8972f203deb221c0838163e06f6d
15
15
 
16
16
  COCOAPODS: 1.12.1
@@ -0,0 +1,37 @@
1
+ //
2
+ // ButtonInfo.swift
3
+ // FireworkSdk
4
+ //
5
+ // Created by linjie jiang on 7/28/23.
6
+ //
7
+
8
+ import Foundation
9
+ import FireworkVideo
10
+
11
+ @objc
12
+ public class ButtonInfo: NSObject, Codable {
13
+ var imageName: String?
14
+ var systemImageName: String?
15
+ var tintColor: String?
16
+
17
+ public func getButtonDisplayConfiguration() -> ButtonDisplayConfiguration? {
18
+ if let imageName = imageName, let image = UIImage(named: imageName) {
19
+ var config = ButtonDisplayConfiguration()
20
+ config.displayMode = .image(image)
21
+ return config
22
+ }
23
+
24
+ if #available(iOS 13.0, *) {
25
+ if let systemImageName = systemImageName, let image = UIImage(systemName: systemImageName) {
26
+ var config = ButtonDisplayConfiguration()
27
+ config.displayMode = .image(image)
28
+ if let tintColor = tintColor?.uicolor() {
29
+ config.tintColor = tintColor
30
+ }
31
+ return config
32
+ }
33
+ }
34
+
35
+ return nil
36
+ }
37
+ }
@@ -43,6 +43,7 @@ enum ShoppingEventName: String, CaseIterable {
43
43
  case clickCartIcon = "fw:shopping:click-cart-icon"
44
44
  case logMessage = "fw:log-message"
45
45
  case customLinkButtonClick = "fw:shopping:custom-link-button-click"
46
+ case customProductCardTap = "fw:shopping:custom-product-card-tap"
46
47
  }
47
48
 
48
49
  /// Live stream event
@@ -24,7 +24,7 @@ extension FireworkVideoSDKError {
24
24
  }
25
25
 
26
26
  extension VideoPlaybackDetails {
27
- var jsObject: [String: Any?] {
27
+ var jsObject: [String: Any] {
28
28
  [
29
29
  "videoId": videoID,
30
30
  "duration": duration,
@@ -149,3 +149,13 @@ extension StoryBlockError {
149
149
  return error
150
150
  }
151
151
  }
152
+
153
+ extension SelectedProductVariant {
154
+ var jsObject: [String: Any] {
155
+ return [
156
+ "url": url?.absoluteString ?? "",
157
+ "productId": productID,
158
+ "unitId": unitID
159
+ ]
160
+ }
161
+ }
@@ -34,6 +34,24 @@ extension RCTConvert {
34
34
  }
35
35
  }
36
36
 
37
+ static func ctaButtonStyle(_ style: [String: AnyObject]?) -> ButtonContentConfiguration? {
38
+ guard let rStyle = style else {
39
+ return nil
40
+ }
41
+ var btnContentConfig = ButtonContentConfiguration()
42
+ if let backgroundColor = rStyle["backgroundColor"] as? String, let color = backgroundColor.uicolor() {
43
+ btnContentConfig.backgroundColor = color
44
+ }
45
+ if let textColor = rStyle["textColor"] as? String, let color = textColor.uicolor() {
46
+ btnContentConfig.textColor = color
47
+ }
48
+ if let fontSize = rStyle["fontSize"] as? Double {
49
+ btnContentConfig.font = UIFont.systemFont(ofSize: fontSize)
50
+ }
51
+
52
+ return btnContentConfig
53
+ }
54
+
37
55
  static func adBadgeConfiguration(_ config: [String: Any]?) -> AdBadgeConfiguration? {
38
56
  guard let rConfig = config else {
39
57
  return nil
@@ -15,7 +15,8 @@ extension StoryBlockSourceType {
15
15
  "playlist": .playlist,
16
16
  "dynamicContent": .dynamicContent,
17
17
  "hashtagPlaylist": .hashtagPlaylist,
18
- "sku": .sku
18
+ "sku": .sku,
19
+ "singleContent": .singleContent
19
20
  ]
20
21
  }
21
22
  }
@@ -17,7 +17,8 @@ extension VideFeedSourceType {
17
17
  "playlistGroup": .playlistGroup,
18
18
  "dynamicContent": .dynamicContent,
19
19
  "hashtagPlaylist": .hashtagPlaylist,
20
- "sku": .sku
20
+ "sku": .sku,
21
+ "singleContent": .singleContent
21
22
  ]
22
23
  }
23
24
  }
@@ -263,11 +263,11 @@ extension FireworkSDKModule {
263
263
  }
264
264
 
265
265
  var fwAdBadgeConfiguration = FireworkVideo.AdBadgeConfiguration()
266
- if let textColor = adBadgeConfiguration.textColor {
267
- fwAdBadgeConfiguration.textColor = textColor.uicolor()
266
+ if let textColor = adBadgeConfiguration.textColor, let color = textColor.uicolor() {
267
+ fwAdBadgeConfiguration.textColor = color
268
268
  }
269
- if let backgroundColor = adBadgeConfiguration.backgroundColor {
270
- fwAdBadgeConfiguration.backgroundColor = backgroundColor.uicolor()
269
+ if let backgroundColor = adBadgeConfiguration.backgroundColor, let color = backgroundColor.uicolor() {
270
+ fwAdBadgeConfiguration.backgroundColor = color
271
271
  }
272
272
  switch adBadgeConfiguration.badgeTextType {
273
273
  case .sponsored:
@@ -10,6 +10,7 @@ import Foundation
10
10
  struct ProductInfoViewConfiguration: Codable {
11
11
  var ctaButton: CTAButtonConfiguration?
12
12
  var linkButton: LinkButtonConfiguration?
13
+ var productCard: ProductCardConfiguration?
13
14
 
14
15
  struct CTAButtonConfiguration: Codable {
15
16
  var text: CTAButtonText?
@@ -26,4 +27,19 @@ struct ProductInfoViewConfiguration: Codable {
26
27
  struct LinkButtonConfiguration: Codable {
27
28
  var isHidden: Bool?
28
29
  }
30
+
31
+ struct ProductCardConfiguration: Codable {
32
+ var theme: ProductCardTheme?
33
+ var ctaButtonText: ProductCardCTAButtonText?
34
+ }
35
+
36
+ enum ProductCardTheme: String, Codable {
37
+ case light
38
+ case dark
39
+ }
40
+
41
+ enum ProductCardCTAButtonText: String, Codable {
42
+ case shopNow
43
+ case buyNow
44
+ }
29
45
  }
@@ -17,5 +17,6 @@ RCT_EXTERN_METHOD(setProductInfoViewConfiguration:(NSDictionary *)config)
17
17
  RCT_EXTERN_METHOD(clearCallbackId:(nonnull NSNumber *)cbId eventName:(nonnull NSString *)name)
18
18
  RCT_EXTERN_METHOD(setCustomClickLinkButtonEnabled:(BOOL)enabled resolver: (RCTPromiseResolveBlock)resolver rejecter:(RCTPromiseRejectBlock)rejecter)
19
19
  RCT_EXTERN_METHOD(updateShoppingCTAResult:(NSDictionary *)result cbId:(nonnull NSNumber *)cbId)
20
+ RCT_EXTERN_METHOD(setCustomTapProductCardEnabled:(BOOL)enabled resolver: (RCTPromiseResolveBlock)resolver rejecter:(RCTPromiseRejectBlock)rejecter)
20
21
 
21
22
  @end
@@ -5,8 +5,7 @@
5
5
  // Created by Jeff Zheng on 2021/12/30.
6
6
  //
7
7
 
8
- // swiftlint:disable file_length
9
-
8
+ // swiftlint:disable file_length type_body_length
10
9
  import FireworkVideo
11
10
  import Foundation
12
11
 
@@ -21,6 +20,7 @@ class ShoppingModule: RCTEventEmitter, FireworkVideoShoppingDelegate {
21
20
  private var itemCounts = 0
22
21
  private var productInfoViewConfiguration: ProductInfoViewConfiguration?
23
22
  private var customClickLinkButtonEnabled = false
23
+ private var customTapProductCardEnabled = false
24
24
 
25
25
  override func supportedEvents() -> [String]! {
26
26
  ShoppingEventName.allCases.map { $0.rawValue }
@@ -168,6 +168,15 @@ class ShoppingModule: RCTEventEmitter, FireworkVideoShoppingDelegate {
168
168
  resolver([:])
169
169
  }
170
170
 
171
+ @objc
172
+ func setCustomTapProductCardEnabled(
173
+ _ enabled: Bool, resolver: @escaping RCTPromiseResolveBlock,
174
+ rejecter: @escaping RCTPromiseRejectBlock
175
+ ) {
176
+ customTapProductCardEnabled = enabled
177
+ resolver([:])
178
+ }
179
+
171
180
  @objc
172
181
  func updateShoppingCTAResult(_ result: [String: Any]?, cbId: NSNumber) {
173
182
  guard let ctaResult = RCTConvert.buildShoppingCTAResult(result) else {
@@ -264,14 +273,11 @@ class ShoppingModule: RCTEventEmitter, FireworkVideoShoppingDelegate {
264
273
  ) {
265
274
  let callbackId = ShoppingModule.generateCallbackId()
266
275
  self.shoppingCTAHandlerMap[callbackId] = ctaCompletionHandler
276
+ var body = item.jsObject
277
+ body["callbackId"] = callbackId
267
278
  sendEvent(
268
279
  withName: ShoppingEventName.ctaButtonClick.rawValue,
269
- body: [
270
- "url": item.url?.absoluteString ?? "",
271
- "productId": item.productID,
272
- "unitId": item.unitID,
273
- "callbackId": callbackId
274
- ])
280
+ body: body)
275
281
  }
276
282
 
277
283
  func fireworkShopping(
@@ -282,11 +288,7 @@ class ShoppingModule: RCTEventEmitter, FireworkVideoShoppingDelegate {
282
288
  if customClickLinkButtonEnabled {
283
289
  sendEvent(
284
290
  withName: ShoppingEventName.customLinkButtonClick.rawValue,
285
- body: [
286
- "url": itemURL,
287
- "productId": item.productID,
288
- "unitId": item.unitID
289
- ])
291
+ body: item.jsObject)
290
292
  }
291
293
  return customClickLinkButtonEnabled
292
294
  }
@@ -298,9 +300,26 @@ class ShoppingModule: RCTEventEmitter, FireworkVideoShoppingDelegate {
298
300
  sendEvent(
299
301
  withName: ShoppingEventName.clickCartIcon.rawValue, body: [:])
300
302
  }
303
+
304
+ func fireworkShopping(
305
+ _ fireworkShopping: FireworkVideoShopping,
306
+ didTapProductVariant item: SelectedProductVariant,
307
+ forVideo video: VideoDetails
308
+ ) -> Bool {
309
+ if customTapProductCardEnabled {
310
+ var body = item.jsObject
311
+ body["video"] = video.jsObject
312
+ sendEvent(
313
+ withName: ShoppingEventName.customProductCardTap.rawValue,
314
+ body: body)
315
+
316
+ }
317
+ return customTapProductCardEnabled
318
+ }
301
319
  }
302
320
 
303
321
  extension ShoppingModule {
322
+ // swiftlint:disable:next cyclomatic_complexity
304
323
  fileprivate static func hydrateProductViewConfig(
305
324
  _ config: ProductInfoViewConfiguration,
306
325
  _ productInfoViewConfigurator: ProductInfoViewConfigurable
@@ -317,13 +336,15 @@ extension ShoppingModule {
317
336
  }
318
337
  }
319
338
  if let backgroundColor = ctaButtonConfiguration.backgroundColor,
320
- backgroundColor.count > 0 {
321
- productDetailsConfiguration.ctaButton.buttonConfiguration.backgroundColor = backgroundColor.uicolor()
339
+ backgroundColor.count > 0,
340
+ let color = backgroundColor.uicolor() {
341
+ productDetailsConfiguration.ctaButton.buttonConfiguration.backgroundColor = color
322
342
  }
323
343
 
324
344
  if let textColor = ctaButtonConfiguration.textColor,
325
- textColor.count > 0 {
326
- productDetailsConfiguration.ctaButton.buttonConfiguration.textColor = textColor.uicolor()
345
+ textColor.count > 0,
346
+ let color = textColor.uicolor() {
347
+ productDetailsConfiguration.ctaButton.buttonConfiguration.textColor = color
327
348
  }
328
349
 
329
350
  if let fontSize = ctaButtonConfiguration.fontSize {
@@ -338,6 +359,26 @@ extension ShoppingModule {
338
359
  }
339
360
  }
340
361
 
362
+ if let productCardConfiguration = config.productCard {
363
+ if let theme = productCardConfiguration.theme {
364
+ switch theme {
365
+ case .light:
366
+ productInfoViewConfigurator.productCardConfiguration.appearance = .light
367
+ case .dark:
368
+ productInfoViewConfigurator.productCardConfiguration.appearance = .dark
369
+ }
370
+ }
371
+
372
+ if let text = productCardConfiguration.ctaButtonText {
373
+ switch text {
374
+ case .shopNow:
375
+ productInfoViewConfigurator.productCardConfiguration.ctaConfig.text = .shopNow
376
+ case .buyNow:
377
+ productInfoViewConfigurator.productCardConfiguration.ctaConfig.text = .buyNow
378
+ }
379
+ }
380
+ }
381
+
341
382
  productInfoViewConfigurator.productDetailsConfiguration = productDetailsConfiguration
342
383
  }
343
384
 
@@ -8,39 +8,65 @@
8
8
  import UIKit
9
9
 
10
10
  extension String {
11
+ func uicolor() -> UIColor? {
12
+ var hex = self.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines).uppercased()
11
13
 
12
- func uicolor(alpha: CGFloat = 1.0) -> UIColor {
13
- var red: UInt64 = 0
14
- var green: UInt64 = 0
15
- var blue: UInt64 = 0
16
- var hex = self
17
- //
18
- if hex.hasPrefix("0x") || hex.hasPrefix("0X") {
14
+ guard matchHexColorWithLength(hex) else {
15
+ return nil
16
+ }
17
+
18
+ if hex.hasPrefix("0X") {
19
19
  hex = String(hex[hex.index(hex.startIndex, offsetBy: 2)...])
20
20
  } else if hex.hasPrefix("#") {
21
21
  hex = String(hex[hex.index(hex.startIndex, offsetBy: 1)...])
22
22
  }
23
- //
24
- if hex.count < 6 {
25
- for _ in 0..<6 - hex.count {
26
- hex += "0"
27
- }
23
+
24
+ if hex.count == 3 {
25
+ hex = convertToDoubleChar(from: hex)
26
+ hex += "FF"
27
+ }
28
+
29
+ if hex.count == 4 {
30
+ hex = convertToDoubleChar(from: hex)
31
+ }
32
+
33
+ if hex.count == 6 {
34
+ hex += "FF"
28
35
  }
29
36
 
30
- // convert
31
- // red
32
- Scanner(string: String(hex[..<hex.index(hex.startIndex, offsetBy: 2)])).scanHexInt64(&red)
33
- // green
34
- Scanner(
35
- string: String(
36
- hex[hex.index(hex.startIndex, offsetBy: 2)..<hex.index(hex.startIndex, offsetBy: 4)]
37
- )
38
- ).scanHexInt64(&green)
39
- // blue
40
- Scanner(string: String(hex[hex.index(startIndex, offsetBy: 4)...])).scanHexInt64(&blue)
41
-
42
- return UIColor(
43
- red: CGFloat(red) / 255.0, green: CGFloat(green) / 255.0, blue: CGFloat(blue) / 255.0,
44
- alpha: alpha)
37
+ var rgbaValue: UInt64 = 0
38
+ Scanner(string: hex).scanHexInt64(&rgbaValue)
39
+
40
+ let red = CGFloat((rgbaValue & 0xFF000000) >> 24) / 255.0
41
+ let green = CGFloat((rgbaValue & 0x00FF0000) >> 16) / 255.0
42
+ let blue = CGFloat((rgbaValue & 0x0000FF00) >> 8) / 255.0
43
+ let alpha = CGFloat(rgbaValue & 0x000000FF) / 255.0
44
+
45
+ return UIColor(red: red, green: green, blue: blue, alpha: alpha)
46
+
47
+ }
48
+
49
+ private func convertToDoubleChar(from input: String) -> String {
50
+ var result = ""
51
+
52
+ for char in input {
53
+ let repeatedChar = String(repeating: char, count: 2)
54
+ result += repeatedChar
55
+ }
56
+
57
+ return result
58
+ }
59
+
60
+ private func matchHexColorWithLength(_ input: String) -> Bool {
61
+ do {
62
+ let pattern = "^(0X|#)?([0-9A-Fa-f]{3}|[0-9A-Fa-f]{4}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$"
63
+ let regex = try NSRegularExpression(pattern: pattern)
64
+ let range = NSRange(location: 0, length: input.utf16.count)
65
+ let matches = regex.matches(in: input, range: range)
66
+
67
+ return matches.count > 0
68
+ } catch {
69
+ return false
70
+ }
45
71
  }
46
72
  }
@@ -9,10 +9,6 @@ var _FWEventName = require("./models/FWEventName");
9
9
 
10
10
  var _FWNavigatorModule = _interopRequireWildcard(require("./modules/FWNavigatorModule"));
11
11
 
12
- var _FWLoggerUtil = _interopRequireDefault(require("./utils/FWLoggerUtil"));
13
-
14
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15
-
16
12
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
17
13
 
18
14
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
@@ -32,8 +28,6 @@ class FWNavigator {
32
28
  }
33
29
 
34
30
  constructor() {
35
- _FWLoggerUtil.default.log('FWNavigator constructor');
36
-
37
31
  _FWNavigatorModule.FWNavigatorModuleEventEmitter.addListener(_FWEventName.FWEventName.LogMessage, () => {});
38
32
  }
39
33
  /**
@@ -1 +1 @@
1
- {"version":3,"sources":["FWNavigator.ts"],"names":["FWNavigator","getInstance","_instance","constructor","FWLoggerUtil","log","FWNavigatorModuleEventEmitter","addListener","FWEventName","LogMessage","pushRNContainer","params","FWNavigatorModule","popRNContainer","popNativeContainer","startFloatingPlayer","stopFloatingPlayer"],"mappings":";;;;;;;AAAA;;AAEA;;AAGA;;;;;;;;;;AAEA;AACA;AACA;AACA,MAAMA,WAAN,CAAkB;AAGS,SAAXC,WAAW,GAAgB;AACvC,QAAI,CAACD,WAAW,CAACE,SAAjB,EAA4B;AAC1BF,MAAAA,WAAW,CAACE,SAAZ,GAAwB,IAAIF,WAAJ,EAAxB;AACD;;AACD,WAAOA,WAAW,CAACE,SAAnB;AACD;;AAEOC,EAAAA,WAAW,GAAG;AACpBC,0BAAaC,GAAb,CAAiB,yBAAjB;;AACAC,qDAA8BC,WAA9B,CAA0CC,yBAAYC,UAAtD,EAAkE,MAAM,CAAE,CAA1E;AACD;AAED;AACF;AACA;AACA;AACA;AACA;AACA;;;AACSC,EAAAA,eAAe,CAACC,MAAD,EAAkD;AACtE,WAAOC,2BAAkBF,eAAlB,CAAkCC,MAAlC,CAAP;AACD;AAED;AACF;AACA;AACA;;;AACSE,EAAAA,cAAc,GAAqB;AACxC,WAAOD,2BAAkBC,cAAlB,EAAP;AACD;AAED;AACF;AACA;AACA;AACA;AACA;;;AACSC,EAAAA,kBAAkB,GAAqB;AAC5C,WAAOF,2BAAkBE,kBAAlB,EAAP;AACD;AAED;AACF;AACA;AACA;AACA;;;AACkC,QAAnBC,mBAAmB,GAAqB;AACnD,WAAOH,2BAAkBG,mBAAlB,EAAP;AACD;AAED;AACF;AACA;;;AACiC,QAAlBC,kBAAkB,GAAkB;AAC/C,UAAMJ,2BAAkBI,kBAAlB,EAAN;AACD;;AA1De;;gBAAZhB,W;;eA6DSA,W","sourcesContent":["import { FWEventName } from './models/FWEventName';\nimport type PushRNContainerParams from './models/PushRNContainerParams';\nimport FWNavigatorModule, {\n FWNavigatorModuleEventEmitter,\n} from './modules/FWNavigatorModule';\nimport FWLoggerUtil from './utils/FWLoggerUtil';\n\n/**\n * You can use this class for pushing RN page from the native page.\n */\nclass FWNavigator {\n private static _instance?: FWNavigator;\n\n public static getInstance(): FWNavigator {\n if (!FWNavigator._instance) {\n FWNavigator._instance = new FWNavigator();\n }\n return FWNavigator._instance!;\n }\n\n private constructor() {\n FWLoggerUtil.log('FWNavigator constructor');\n FWNavigatorModuleEventEmitter.addListener(FWEventName.LogMessage, () => {});\n }\n\n /**\n * Push a new RN container.\n * You could use this method to make a RN page on top of\n * SDK full-screen player.\n * @param params {PushRNContainerParams}\n * @returns {Promise<boolean>} The result of pushing a new Container.\n */\n public pushRNContainer(params: PushRNContainerParams): Promise<boolean> {\n return FWNavigatorModule.pushRNContainer(params);\n }\n\n /**\n * Pop the RN container you pushed before.\n * @returns {Promise<boolean>} The result of popping a new Container.\n */\n public popRNContainer(): Promise<boolean> {\n return FWNavigatorModule.popRNContainer();\n }\n\n /**\n * Pop the native container. You could use the method to close the fullscreen player.\n * But if the enablePictureInPicture of the associated video feed is true,\n * we can't close the fullscreen player.\n * @returns {Promise<boolean>} The result of popping the native container.\n */\n public popNativeContainer(): Promise<boolean> {\n return FWNavigatorModule.popNativeContainer();\n }\n\n /**\n * Change current fullscreen player to floating player.\n * @returns {Promise<boolean>} If the result is true, it means that the fullscreen\n * player is changed to floating player.\n */\n public async startFloatingPlayer(): Promise<boolean> {\n return FWNavigatorModule.startFloatingPlayer();\n }\n\n /**\n * Stop current floating player.\n */\n public async stopFloatingPlayer(): Promise<void> {\n await FWNavigatorModule.stopFloatingPlayer();\n }\n}\n\nexport default FWNavigator;\n"]}
1
+ {"version":3,"sources":["FWNavigator.ts"],"names":["FWNavigator","getInstance","_instance","constructor","FWNavigatorModuleEventEmitter","addListener","FWEventName","LogMessage","pushRNContainer","params","FWNavigatorModule","popRNContainer","popNativeContainer","startFloatingPlayer","stopFloatingPlayer"],"mappings":";;;;;;;AAAA;;AAEA;;;;;;;;AAIA;AACA;AACA;AACA,MAAMA,WAAN,CAAkB;AAGS,SAAXC,WAAW,GAAgB;AACvC,QAAI,CAACD,WAAW,CAACE,SAAjB,EAA4B;AAC1BF,MAAAA,WAAW,CAACE,SAAZ,GAAwB,IAAIF,WAAJ,EAAxB;AACD;;AACD,WAAOA,WAAW,CAACE,SAAnB;AACD;;AAEOC,EAAAA,WAAW,GAAG;AACpBC,qDAA8BC,WAA9B,CAA0CC,yBAAYC,UAAtD,EAAkE,MAAM,CAAE,CAA1E;AACD;AAED;AACF;AACA;AACA;AACA;AACA;AACA;;;AACSC,EAAAA,eAAe,CAACC,MAAD,EAAkD;AACtE,WAAOC,2BAAkBF,eAAlB,CAAkCC,MAAlC,CAAP;AACD;AAED;AACF;AACA;AACA;;;AACSE,EAAAA,cAAc,GAAqB;AACxC,WAAOD,2BAAkBC,cAAlB,EAAP;AACD;AAED;AACF;AACA;AACA;AACA;AACA;;;AACSC,EAAAA,kBAAkB,GAAqB;AAC5C,WAAOF,2BAAkBE,kBAAlB,EAAP;AACD;AAED;AACF;AACA;AACA;AACA;;;AACkC,QAAnBC,mBAAmB,GAAqB;AACnD,WAAOH,2BAAkBG,mBAAlB,EAAP;AACD;AAED;AACF;AACA;;;AACiC,QAAlBC,kBAAkB,GAAkB;AAC/C,UAAMJ,2BAAkBI,kBAAlB,EAAN;AACD;;AAzDe;;gBAAZd,W;;eA4DSA,W","sourcesContent":["import { FWEventName } from './models/FWEventName';\nimport type PushRNContainerParams from './models/PushRNContainerParams';\nimport FWNavigatorModule, {\n FWNavigatorModuleEventEmitter,\n} from './modules/FWNavigatorModule';\n\n/**\n * You can use this class for pushing RN page from the native page.\n */\nclass FWNavigator {\n private static _instance?: FWNavigator;\n\n public static getInstance(): FWNavigator {\n if (!FWNavigator._instance) {\n FWNavigator._instance = new FWNavigator();\n }\n return FWNavigator._instance!;\n }\n\n private constructor() {\n FWNavigatorModuleEventEmitter.addListener(FWEventName.LogMessage, () => {});\n }\n\n /**\n * Push a new RN container.\n * You could use this method to make a RN page on top of\n * SDK full-screen player.\n * @param params {PushRNContainerParams}\n * @returns {Promise<boolean>} The result of pushing a new Container.\n */\n public pushRNContainer(params: PushRNContainerParams): Promise<boolean> {\n return FWNavigatorModule.pushRNContainer(params);\n }\n\n /**\n * Pop the RN container you pushed before.\n * @returns {Promise<boolean>} The result of popping a new Container.\n */\n public popRNContainer(): Promise<boolean> {\n return FWNavigatorModule.popRNContainer();\n }\n\n /**\n * Pop the native container. You could use the method to close the fullscreen player.\n * But if the enablePictureInPicture of the associated video feed is true,\n * we can't close the fullscreen player.\n * @returns {Promise<boolean>} The result of popping the native container.\n */\n public popNativeContainer(): Promise<boolean> {\n return FWNavigatorModule.popNativeContainer();\n }\n\n /**\n * Change current fullscreen player to floating player.\n * @returns {Promise<boolean>} If the result is true, it means that the fullscreen\n * player is changed to floating player.\n */\n public async startFloatingPlayer(): Promise<boolean> {\n return FWNavigatorModule.startFloatingPlayer();\n }\n\n /**\n * Stop current floating player.\n */\n public async stopFloatingPlayer(): Promise<void> {\n await FWNavigatorModule.stopFloatingPlayer();\n }\n}\n\nexport default FWNavigator;\n"]}
@@ -62,6 +62,25 @@ class VideoShopping {
62
62
  _ShoppingModule.default.setCustomClickLinkButtonEnabled(!!value);
63
63
  }
64
64
 
65
+ /**
66
+ * This callback is triggered when the user clicks product card.
67
+ * Only supported on iOS.
68
+ *
69
+ * The host app can customize the tap event processing logic of
70
+ * the product card by setting the callback.
71
+ */
72
+ get onCustomTapProductCard() {
73
+ return this._onCustomTapProductCard;
74
+ }
75
+
76
+ set onCustomTapProductCard(value) {
77
+ this._onCustomTapProductCard = value;
78
+
79
+ if (_reactNative.Platform.OS === 'ios') {
80
+ _ShoppingModule.default.setCustomTapProductCardEnabled(!!value);
81
+ }
82
+ }
83
+
65
84
  /**
66
85
  * Defaults to true.
67
86
  * You can hide the cart icon by setting this property to false.
@@ -125,6 +144,8 @@ class VideoShopping {
125
144
 
126
145
  _defineProperty(this, "_onCustomClickLinkButton", void 0);
127
146
 
147
+ _defineProperty(this, "_onCustomTapProductCard", void 0);
148
+
128
149
  _defineProperty(this, "_cartIconVisible", true);
129
150
 
130
151
  _defineProperty(this, "_productInfoViewConfiguration", void 0);
@@ -150,6 +171,11 @@ class VideoShopping {
150
171
 
151
172
  this.handleCustomLinkButtonClickEvent(event);
152
173
  });
174
+ this.eventEmitter.addListener(_FWEventName.FWEventName.CustomProductCardTap, event => {
175
+ _FWLoggerUtil.default.log(`Receive CustomProductCardTap event url: ${event === null || event === void 0 ? void 0 : event.url}`);
176
+
177
+ this.handleCustomProductCardTapEvent(event);
178
+ });
153
179
  }
154
180
  /**
155
181
  *
@@ -238,6 +264,12 @@ class VideoShopping {
238
264
  }
239
265
  }
240
266
 
267
+ async handleCustomProductCardTapEvent(event) {
268
+ if (this.onCustomTapProductCard) {
269
+ this.onCustomTapProductCard(event);
270
+ }
271
+ }
272
+
241
273
  }
242
274
 
243
275
  _defineProperty(VideoShopping, "_instance", void 0);