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
@@ -16,6 +16,7 @@ enum FWEventName: String, CaseIterable {
16
16
  case shareBaseURLUpdated = "fw:share-base-url-updated" // emitted in JS side
17
17
  case videoLaunchBehaviorUpdated = "fw:video-launch-behavior-updated" // emitted in JS side
18
18
  case adBadgeConfigurationUpdated = "fw:ad-badge-configuration-updated" // emitted in JS side
19
+ case appLanguageUpdated = "fw:app-language-updated" // emitted in JS side
19
20
  case logMessage = "fw:log-message"
20
21
  }
21
22
 
@@ -35,9 +36,10 @@ enum VideoPlaybackSubEventName: String {
35
36
  enum ShoppingEventName: String, CaseIterable {
36
37
  case updateProductDetails = "fw:shopping:update-product-details"
37
38
  case willDisplayProduct = "fw:shopping:will-display-product"
38
- case addToCart = "fw:shopping:add-to-cart"
39
+ case ctaButtonClick = "fw:shopping:cta-button-click"
39
40
  case clickCartIcon = "fw:shopping:click-cart-icon"
40
41
  case logMessage = "fw:log-message"
42
+ case customLinkButtonClick = "fw:shopping:custom-link-button-click"
41
43
  }
42
44
 
43
45
  /// Live stream event
@@ -63,4 +63,25 @@ extension RCTConvert {
63
63
 
64
64
  return result
65
65
  }
66
+
67
+ static func buildShoppingCTAResult(_ result: [String: Any]?)
68
+ -> ShoppingCTAResult? {
69
+ guard let rResult = result else {
70
+ return nil
71
+ }
72
+
73
+ let jsonData = try? JSONSerialization.data(withJSONObject: rResult, options: .prettyPrinted)
74
+ guard let rJsonData = jsonData else {
75
+ return nil
76
+ }
77
+
78
+ var ctaResult: ShoppingCTAResult?
79
+ do {
80
+ ctaResult = try JSONDecoder().decode(ShoppingCTAResult.self, from: rJsonData)
81
+ } catch let error {
82
+ print(error.localizedDescription)
83
+ }
84
+
85
+ return ctaResult
86
+ }
66
87
  }
@@ -6,6 +6,7 @@
6
6
  //
7
7
 
8
8
  import Foundation
9
+ import FireworkVideo
9
10
 
10
11
  extension VideFeedSourceType {
11
12
  static var sourceTypeMapper: [String: VideFeedSourceType] {
@@ -87,4 +88,30 @@ extension RCTConvert {
87
88
 
88
89
  return try? JSONDecoder().decode(AdConfiguration.self, from: rJsonData)
89
90
  }
91
+
92
+ @nonobjc
93
+ public static func getFireworkVideoAdConfiguration(
94
+ _ adConfiguration: AdConfiguration?
95
+ ) -> FireworkVideo.AdConfiguration? {
96
+ guard let feedAdConfiguration = adConfiguration else {
97
+ return nil
98
+ }
99
+
100
+ var resultAdConfiguration = FireworkVideo.AdConfiguration()
101
+ if let requiresAds = feedAdConfiguration.requiresAds {
102
+ resultAdConfiguration.requiresAds = requiresAds
103
+ }
104
+
105
+ if let adsFetchTimeout = feedAdConfiguration.adsFetchTimeout {
106
+ resultAdConfiguration.adsFetchTimeout = adsFetchTimeout
107
+ }
108
+
109
+ if let vastAttributes = feedAdConfiguration.vastAttributes {
110
+ resultAdConfiguration.vastAttributes = vastAttributes.map({ attribute in
111
+ return URLQueryItem(name: attribute.name ?? "", value: attribute.value ?? "")
112
+ })
113
+ }
114
+
115
+ return resultAdConfiguration
116
+ }
90
117
  }
@@ -9,6 +9,11 @@
9
9
 
10
10
  import FireworkVideo
11
11
 
12
+ private let FWExitButtonImageNames: [String] = [
13
+ "closeX",
14
+ "down-arrow-light"
15
+ ]
16
+
12
17
  private struct PlayerInfo {
13
18
  enum PlayerMode: Int {
14
19
  case fullscreen, floating
@@ -358,7 +363,6 @@ extension FWNavigatorModule {
358
363
  if let rootVC = keyWindow.rootViewController,
359
364
  let presentedVC = rootVC.presentedViewController {
360
365
  let furthestAncestorController = getFurthestAncestorController(presentedVC)
361
-
362
366
  if let playerVC = getPlayerViewController(furthestAncestorController) {
363
367
  return PlayerInfo(playerVC: playerVC, mode: .fullscreen)
364
368
  }
@@ -19,5 +19,6 @@ RCT_EXTERN_METHOD(setVideoPlaybackEventEnabled:(BOOL)enabled)
19
19
  RCT_EXTERN_METHOD(setAdBadgeConfiguration:(NSDictionary *)config resolver: (RCTPromiseResolveBlock)resolver rejecter:(RCTPromiseRejectBlock)rejecter)
20
20
  RCT_EXTERN_METHOD(setAppComponentName:(NSString *)name resolver: (RCTPromiseResolveBlock)resolver rejecter:(RCTPromiseRejectBlock)rejecter)
21
21
  RCT_EXTERN_METHOD(trackPurchase:(NSDictionary *)parameters)
22
+ RCT_EXTERN_METHOD(changeAppLanguage:(NSString * __nullable)language resolver: (RCTPromiseResolveBlock)resolver rejecter:(RCTPromiseRejectBlock)rejecter)
22
23
 
23
24
  @end
@@ -44,6 +44,18 @@ class FireworkSDKModule: RCTEventEmitter, FireworkVideoSDKDelegate {
44
44
  #if DEBUG
45
45
  let formatter = DateFormatter()
46
46
  formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
47
+ let iOSSDKBundle = Bundle(for: FireworkVideoSDK.self)
48
+
49
+ let messageForLocalizations = """
50
+ [iOS] localizations information \(formatter.string(from: Date()))
51
+ AppleLanguages: \((UserDefaults.standard.object(forKey: "AppleLanguages") as? [String]) ?? [])
52
+ Bundle.main.localizations \(Bundle.main.localizations)
53
+ Bundle.main.preferredLocalizations \(Bundle.main.preferredLocalizations)
54
+ iOSSDKBundle.localizations \(iOSSDKBundle.localizations)
55
+ iOSSDKBundle.preferredLocalizations \(iOSSDKBundle.preferredLocalizations)
56
+ """
57
+ print("[react-native-firework-sdk] [swift] \(messageForLocalizations)")
58
+ sendEvent(withName: FWEventName.logMessage.rawValue, body: ["message": messageForLocalizations])
47
59
 
48
60
  let message = "[iOS] Call initializeSDK \(formatter.string(from: Date()))"
49
61
  print("[react-native-firework-sdk] [swift] \(message)")
@@ -59,6 +71,7 @@ class FireworkSDKModule: RCTEventEmitter, FireworkVideoSDKDelegate {
59
71
  FireworkVideoSDK.eventTracking.videoPlaybackDelegate = self
60
72
  FireworkVideoSDK.eventTracking.feedDelegate = self
61
73
 
74
+ FWAppLanguageManager.shared.initializeManager()
62
75
  resolver([:])
63
76
  }
64
77
 
@@ -157,6 +170,23 @@ class FireworkSDKModule: RCTEventEmitter, FireworkVideoSDKDelegate {
157
170
  )
158
171
  }
159
172
 
173
+ @objc(changeAppLanguage:resolver:rejecter:)
174
+ func changeAppLanguage(
175
+ _ language: String?, resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock
176
+ ) {
177
+ #if DEBUG
178
+ let formatter = DateFormatter()
179
+ formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
180
+ let message = "[iOS] Call changeAppLanguage: \(language ?? "") \(formatter.string(from: Date()))"
181
+ sendEvent(withName: FWEventName.logMessage.rawValue, body: ["message": message])
182
+ #endif
183
+
184
+ DispatchQueue.main.async {
185
+ let result = FWAppLanguageManager.shared.changeAppLanguage(language)
186
+ resolver(result)
187
+ }
188
+ }
189
+
160
190
  // MARK: - FireworkVideoSDKDelegate
161
191
  func fireworkVideoDidLoadSuccessfully() {
162
192
  #if DEBUG
@@ -9,6 +9,7 @@ import Foundation
9
9
 
10
10
  struct ProductInfoViewConfiguration: Codable {
11
11
  var addToCartButton: AddToCartButtonConfiguration?
12
+ var ctaButton: CTAButtonConfiguration?
12
13
  var linkButton: LinkButtonConfiguration?
13
14
 
14
15
  struct AddToCartButtonConfiguration: Codable {
@@ -18,6 +19,18 @@ struct ProductInfoViewConfiguration: Codable {
18
19
  var iOSFontInfo: FontInfo?
19
20
  }
20
21
 
22
+ struct CTAButtonConfiguration: Codable {
23
+ var text: CTAButtonText?
24
+ var backgroundColor: String?
25
+ var textColor: String?
26
+ var fontSize: Double?
27
+ var iOSFontInfo: FontInfo?
28
+ }
29
+
30
+ enum CTAButtonText: String, Codable {
31
+ case addToCart, shopNow
32
+ }
33
+
21
34
  struct LinkButtonConfiguration: Codable {
22
35
  var isHidden: Bool?
23
36
  }
@@ -0,0 +1,16 @@
1
+ //
2
+ // ShoppingCTAResult.swift
3
+ //
4
+ // Created by linjie jiang on 2023/3/20.
5
+ //
6
+
7
+ import Foundation
8
+
9
+ struct ShoppingCTAResult: Codable {
10
+ var res: ShoppingCTAResultRes?
11
+ var tips: String?
12
+
13
+ enum ShoppingCTAResultRes: String, Codable {
14
+ case success, fail
15
+ }
16
+ }
@@ -13,10 +13,11 @@ _RCT_EXTERN_REMAP_METHOD(init, initialize, NO)
13
13
  RCT_EXTERN_METHOD(setCartIconVisible:(BOOL)visible)
14
14
  RCT_EXTERN_METHOD(setCartItemCount:(int)itemCounts)
15
15
  RCT_EXTERN_METHOD(updateVideoProducts:(NSArray *)products cbId:(nonnull NSNumber *)cbId)
16
- RCT_EXTERN_METHOD(updateAddToCartStatus:(NSString *)res tips:(nullable NSString *)tips cbId:(nonnull NSNumber *)cbId)
17
16
  RCT_EXTERN_METHOD(jumpToCartPage:(nonnull NSNumber *)cbId props:(NSDictionary *)props)
18
17
  RCT_EXTERN_METHOD(setCustomClickCartIconEnabled:(BOOL)enabled resolver: (RCTPromiseResolveBlock)resolver rejecter:(RCTPromiseRejectBlock)rejecter)
19
18
  RCT_EXTERN_METHOD(setProductInfoViewConfiguration:(NSDictionary *)config)
20
19
  RCT_EXTERN_METHOD(clearCallbackId:(nonnull NSNumber *)cbId eventName:(nonnull NSString *)name)
20
+ RCT_EXTERN_METHOD(setCustomClickLinkButtonEnabled:(BOOL)enabled resolver: (RCTPromiseResolveBlock)resolver rejecter:(RCTPromiseRejectBlock)rejecter)
21
+ RCT_EXTERN_METHOD(updateShoppingCTAResult:(NSDictionary *)result cbId:(nonnull NSNumber *)cbId)
21
22
 
22
23
  @end
@@ -10,11 +10,13 @@ import Foundation
10
10
 
11
11
  weak var gCartViewController: FWCartViewController?
12
12
 
13
+ // swiftlint:disable file_length type_body_length
14
+
13
15
  @objc(ShoppingModule)
14
16
  class ShoppingModule: RCTEventEmitter, FireworkVideoShoppingDelegate, CartViewControllerProviding {
15
17
 
16
18
  private var productInfoViewConfigurator: (Int, ProductInfoViewConfigurable)?
17
- private var addToCartHandlerMap: [Int: AddToCartHandler] = [:]
19
+ private var shoppingCTAHandlerMap: [Int: ShoppingCTAHandler] = [:]
18
20
  private var productHydratingMap: [Int: ProductHydrating] = [:]
19
21
 
20
22
  private var cartViewController: FWCartViewController?
@@ -22,6 +24,7 @@ class ShoppingModule: RCTEventEmitter, FireworkVideoShoppingDelegate, CartViewCo
22
24
  private var itemCounts = 0
23
25
  private var customClickCartIconEnabled = false
24
26
  private var productInfoViewConfiguration: ProductInfoViewConfiguration?
27
+ private var customClickLinkButtonEnabled = false
25
28
 
26
29
  override func supportedEvents() -> [String]! {
27
30
  ShoppingEventName.allCases.map { $0.rawValue }
@@ -130,21 +133,6 @@ class ShoppingModule: RCTEventEmitter, FireworkVideoShoppingDelegate, CartViewCo
130
133
  }
131
134
  }
132
135
 
133
- @objc
134
- func updateAddToCartStatus(_ res: String, tips: String?, cbId: NSNumber) {
135
- guard let handler = addToCartHandlerMap[Int(truncating: cbId)] else {
136
- return
137
- }
138
-
139
- addToCartHandlerMap.removeValue(forKey: Int(truncating: cbId))
140
-
141
- if res == "success" {
142
- handler(.feedbackOnly(.success(message: tips ?? "success")))
143
- } else {
144
- handler(.feedbackOnly(.failure(message: tips ?? "failure")))
145
- }
146
- }
147
-
148
136
  @objc
149
137
  func jumpToCartPage(_ cbId: NSNumber, props: NSDictionary) {
150
138
  let properties: [String: Any] = (props as? [String: Any]) ?? [:]
@@ -186,9 +174,51 @@ class ShoppingModule: RCTEventEmitter, FireworkVideoShoppingDelegate, CartViewCo
186
174
  func clearCallbackId(_ cbId: NSNumber, eventName: String) {
187
175
  if eventName == ShoppingEventName.updateProductDetails.rawValue {
188
176
  productHydratingMap.removeValue(forKey: Int(truncating: cbId))
189
- } else if eventName == ShoppingEventName.addToCart.rawValue {
190
- addToCartHandlerMap.removeValue(forKey: Int(truncating: cbId))
177
+ } else if eventName == ShoppingEventName.ctaButtonClick.rawValue {
178
+ if let handler = shoppingCTAHandlerMap[Int(truncating: cbId)] {
179
+ handler(.none)
180
+ }
181
+ shoppingCTAHandlerMap.removeValue(forKey: Int(truncating: cbId))
182
+ }
183
+ }
184
+
185
+ @objc
186
+ func setCustomClickLinkButtonEnabled(
187
+ _ enabled: Bool, resolver: @escaping RCTPromiseResolveBlock,
188
+ rejecter: @escaping RCTPromiseRejectBlock
189
+ ) {
190
+ customClickLinkButtonEnabled = enabled
191
+ resolver([:])
192
+ }
193
+
194
+ @objc
195
+ func updateShoppingCTAResult(_ result: [String: Any]?, cbId: NSNumber) {
196
+ guard let ctaResult = RCTConvert.buildShoppingCTAResult(result) else {
197
+ return
198
+ }
199
+
200
+ #if DEBUG
201
+ print("ctaResult res \(String(describing: ctaResult.res))")
202
+ #endif
203
+
204
+ guard let handler = shoppingCTAHandlerMap[Int(truncating: cbId)] else {
205
+ return
191
206
  }
207
+
208
+ if let res = ctaResult.res,
209
+ let tips = ctaResult.tips,
210
+ tips.count > 0 {
211
+ switch res {
212
+ case .success:
213
+ handler(.feedbackOnly(.success(message: tips)))
214
+ case .fail:
215
+ handler(.feedbackOnly(.failure(message: tips)))
216
+ }
217
+ } else {
218
+ handler(.none)
219
+ }
220
+
221
+ shoppingCTAHandlerMap.removeValue(forKey: Int(truncating: cbId))
192
222
  }
193
223
 
194
224
  // MARK: - FireworkVideoShoppingDelegate
@@ -253,15 +283,37 @@ class ShoppingModule: RCTEventEmitter, FireworkVideoShoppingDelegate, CartViewCo
253
283
 
254
284
  func fireworkShopping(
255
285
  _ fireworkShopping: FireworkVideoShopping,
256
- addProductVariantToCart item: SelectedProductVariant, fromVideo video: VideoDetails,
257
- _ addToCartCompletionHandler: @escaping AddToCartHandler
286
+ productVariantCTASelected item: SelectedProductVariant,
287
+ fromVideo video: VideoDetails,
288
+ ctaCompletionHandler: @escaping ShoppingCTAHandler
258
289
  ) {
259
290
  let callbackId = ShoppingModule.generateCallbackId()
260
- self.addToCartHandlerMap[callbackId] = addToCartCompletionHandler
261
-
291
+ self.shoppingCTAHandlerMap[callbackId] = ctaCompletionHandler
262
292
  sendEvent(
263
- withName: ShoppingEventName.addToCart.rawValue,
264
- body: ["productId": item.productID, "unitId": item.unitID, "callbackId": callbackId])
293
+ withName: ShoppingEventName.ctaButtonClick.rawValue,
294
+ body: [
295
+ "url": item.url?.absoluteString ?? "",
296
+ "productId": item.productID,
297
+ "unitId": item.unitID,
298
+ "callbackId": callbackId
299
+ ])
300
+ }
301
+
302
+ func fireworkShopping(
303
+ _ fireworkShopping: FireworkVideoShopping,
304
+ didTapLinkButtonAt item: SelectedProductVariant,
305
+ withURL itemURL: String
306
+ ) -> Bool {
307
+ if customClickLinkButtonEnabled {
308
+ sendEvent(
309
+ withName: ShoppingEventName.customLinkButtonClick.rawValue,
310
+ body: [
311
+ "url": itemURL,
312
+ "productId": item.productID,
313
+ "unitId": item.unitID
314
+ ])
315
+ }
316
+ return customClickLinkButtonEnabled
265
317
  }
266
318
 
267
319
  // MARK: - CartViewControllerProviding
@@ -283,27 +335,52 @@ class ShoppingModule: RCTEventEmitter, FireworkVideoShoppingDelegate, CartViewCo
283
335
  }
284
336
 
285
337
  extension ShoppingModule {
286
-
338
+ // swiftlint:disable:next cyclomatic_complexity
287
339
  fileprivate static func hydrateProductViewConfig(
288
340
  _ config: ProductInfoViewConfiguration,
289
341
  _ productInfoViewConfigurator: ProductInfoViewConfigurable
290
342
  ) {
291
343
  var productDetailsConfiguration = productInfoViewConfigurator.productDetailsConfiguration
292
344
 
293
- if let addToCartButtonConfiguration = config.addToCartButton {
345
+ if let ctaButtonConfiguration = config.ctaButton {
346
+ if let text = ctaButtonConfiguration.text {
347
+ switch text {
348
+ case .addToCart:
349
+ productDetailsConfiguration.ctaButton.text = .addToCart
350
+ case .shopNow:
351
+ productDetailsConfiguration.ctaButton.text = .shopNow
352
+ }
353
+ }
354
+ if let backgroundColor = ctaButtonConfiguration.backgroundColor,
355
+ backgroundColor.count > 0 {
356
+ productDetailsConfiguration.ctaButton.buttonConfiguration.backgroundColor = backgroundColor.uicolor()
357
+ }
358
+
359
+ if let textColor = ctaButtonConfiguration.textColor,
360
+ textColor.count > 0 {
361
+ productDetailsConfiguration.ctaButton.buttonConfiguration.textColor = textColor.uicolor()
362
+ }
363
+
364
+ if let fontSize = ctaButtonConfiguration.fontSize {
365
+ let iOSFontInfo = ctaButtonConfiguration.iOSFontInfo ?? FontInfo()
366
+ productDetailsConfiguration.ctaButton.buttonConfiguration.font = iOSFontInfo.getFont(fontSize)
367
+ }
368
+ } else if let addToCartButtonConfiguration = config.addToCartButton {
369
+ productDetailsConfiguration.ctaButton.text = .addToCart
370
+
294
371
  if let backgroundColor = addToCartButtonConfiguration.backgroundColor,
295
372
  backgroundColor.count > 0 {
296
- productDetailsConfiguration.addToCartButton.backgroundColor = backgroundColor.uicolor()
373
+ productDetailsConfiguration.ctaButton.buttonConfiguration.backgroundColor = backgroundColor.uicolor()
297
374
  }
298
375
 
299
376
  if let textColor = addToCartButtonConfiguration.textColor,
300
377
  textColor.count > 0 {
301
- productDetailsConfiguration.addToCartButton.textColor = textColor.uicolor()
378
+ productDetailsConfiguration.ctaButton.buttonConfiguration.textColor = textColor.uicolor()
302
379
  }
303
380
 
304
381
  if let fontSize = addToCartButtonConfiguration.fontSize {
305
382
  let iOSFontInfo = addToCartButtonConfiguration.iOSFontInfo ?? FontInfo()
306
- productDetailsConfiguration.addToCartButton.font = iOSFontInfo.getFont(fontSize)
383
+ productDetailsConfiguration.ctaButton.buttonConfiguration.font = iOSFontInfo.getFont(fontSize)
307
384
  }
308
385
  }
309
386
 
@@ -381,5 +458,4 @@ extension ShoppingModule {
381
458
  }
382
459
  return result
383
460
  }
384
-
385
461
  }
@@ -0,0 +1,24 @@
1
+ require 'json'
2
+
3
+ packageJsonPath = File.expand_path("#{__dir__}/../../../package.json")
4
+ package = JSON.parse(File.read(packageJsonPath))
5
+
6
+ Pod::Spec.new do |s|
7
+ s.name = 'FWMultiHostStreaming'
8
+ s.version = package['version']
9
+ s.summary = 'Firework Multi Host Streaming'
10
+ s.homepage = package['homepage']
11
+ s.license = 'Apache License, Version 2.0'
12
+ s.authors = package['author']
13
+
14
+ s.platforms = { ios: '13.0' }
15
+ s.source = { git: 'https://github.com/loopsocial/bogano.git', tag: "#{s.version}" }
16
+
17
+ s.swift_version = '5.0'
18
+ s.source_files = 'src/*.{h,m,mm,swift}', 'src/**/*.{h,m,mm,swift}'
19
+
20
+ s.dependency 'AgoraRtcEngine_iOS', '~> 4.1.1'
21
+ s.dependency 'SwiftProtobuf', '~> 1.21.0'
22
+ s.dependency 'FireworkVideoAgoraSupport', '~> 0.2.0'
23
+ s.dependency 'FireworkVideo'
24
+ end
@@ -0,0 +1,17 @@
1
+ //
2
+ // FireworkMultiHostStreaming.swift
3
+ //
4
+ // Created by linjie jiang on 3/31/23.
5
+ //
6
+
7
+ import Foundation
8
+
9
+ import FireworkVideo
10
+ import FireworkVideoAgoraSupport
11
+
12
+ @objc
13
+ public class MultiHostStreamingSDK: NSObject {
14
+ @objc public static func enableMultiHostStreaming() {
15
+ FireworkVideoSDK.enableMultiHostPlayback()
16
+ }
17
+ }
@@ -0,0 +1,58 @@
1
+ //
2
+ // Bundle+FWSwizzle.swift
3
+ //
4
+ // Created by linjie jiang on 2023/2/7.
5
+ //
6
+
7
+ import Foundation
8
+ import FireworkVideo
9
+
10
+ public extension Bundle {
11
+ static func swizzleMethodsForBundle() {
12
+ FWSwizzleUtil.swizzleSelector(
13
+ cls: self,
14
+ originalSelector: #selector(Bundle.localizedString(forKey:value:table:)),
15
+ customSelector: #selector(Bundle.fw_localizedString(forKey:value:table:)))
16
+ }
17
+
18
+ @objc func fw_localizedString(
19
+ forKey key: String,
20
+ value: String?,
21
+ table tableName: String?
22
+ ) -> String {
23
+ if let language = FWAppLanguageManager.shared.appLanguage,
24
+ let languageCode = FWAppLanguageManager.shared.appLanguageCode,
25
+ Bundle(for: FireworkVideoSDK.self) == self {
26
+ let iOSSDKBundle = Bundle(for: FireworkVideoSDK.self)
27
+
28
+ var languageBundlePath: String?
29
+ let defaultLanguageBundlePath = iOSSDKBundle.path(forResource: "Base", ofType: "lproj")
30
+ if languageCode == "en" {
31
+ languageBundlePath = defaultLanguageBundlePath
32
+ } else if let path = iOSSDKBundle.path(forResource: language, ofType: "lproj") {
33
+ languageBundlePath = path
34
+ } else if let path = iOSSDKBundle.path(forResource: languageCode, ofType: "lproj") {
35
+ languageBundlePath = path
36
+ } else {
37
+ let targeLanguageList = iOSSDKBundle.localizations.filter { item in
38
+ return item != "Base" && item != "en"
39
+ }
40
+ if let targeLanguage = targeLanguageList.first(where: { item in
41
+ let targeLanguageCode = FWLanguageUtil.getLanguageCode(item)
42
+ return languageCode == targeLanguageCode
43
+ }) {
44
+ languageBundlePath = iOSSDKBundle.path(forResource: targeLanguage, ofType: "lproj")
45
+ }
46
+ }
47
+
48
+ if let resultLanguageBundlePath = languageBundlePath ?? defaultLanguageBundlePath,
49
+ let resultLanguageBundle = Bundle(path: resultLanguageBundlePath) {
50
+ return resultLanguageBundle.fw_localizedString(forKey: key, value: value, table: tableName)
51
+ } else {
52
+ return self.fw_localizedString(forKey: key, value: value, table: tableName)
53
+ }
54
+ } else {
55
+ return self.fw_localizedString(forKey: key, value: value, table: tableName)
56
+ }
57
+ }
58
+ }
@@ -0,0 +1,139 @@
1
+ //
2
+ // FWAppLanguageManager.swift
3
+ //
4
+ // Created by linjie jiang on 2023/2/7.
5
+ //
6
+
7
+ import Foundation
8
+
9
+ private let appLanguageStorageKey = "firework_sdk_app_language_storage_key"
10
+
11
+ public enum AppLanguageLayoutDirection: Int {
12
+ case ltr, rtl, unsupported
13
+ }
14
+
15
+ public class FWAppLanguageManager {
16
+ public static let shared = FWAppLanguageManager()
17
+
18
+ private var _systemLanguage: String?
19
+ private var _systemLanguageCode: String? {
20
+ guard let language = _systemLanguage else {
21
+ return nil
22
+ }
23
+
24
+ return FWLanguageUtil.getLanguageCode(language)
25
+ }
26
+ private var _hasInitialized: Bool = false
27
+
28
+ public var appLanguage: String? {
29
+ didSet {
30
+ if appLanguage != nil {
31
+ FWAppLanguageManager.swizzelMethods()
32
+ FWRTLManager.sharedInstance().enableHorizontalFlip = shouldHorizontalFlip
33
+ }
34
+ }
35
+ }
36
+
37
+ public var appLanguageCode: String? {
38
+ guard let language = appLanguage else {
39
+ return nil
40
+ }
41
+
42
+ return FWLanguageUtil.getLanguageCode(language)
43
+ }
44
+
45
+ public var appLanguageLayoutDirection: AppLanguageLayoutDirection? {
46
+ guard let languageCode = appLanguageCode else {
47
+ return nil
48
+ }
49
+ let direction = Locale.characterDirection(forLanguage: languageCode)
50
+ switch direction {
51
+ case .leftToRight:
52
+ return .ltr
53
+ case .rightToLeft:
54
+ return .rtl
55
+ default:
56
+ return .unsupported
57
+ }
58
+ }
59
+
60
+ public var shouldHorizontalFlip: Bool {
61
+ guard let appLanguageCode = appLanguageCode else {
62
+ return false
63
+ }
64
+
65
+ guard let systemLanguageCode = _systemLanguageCode else {
66
+ return false
67
+ }
68
+
69
+ let appLanguageDirection = Locale.characterDirection(forLanguage: appLanguageCode)
70
+ let systemLanguageDirection = Locale.characterDirection(forLanguage: systemLanguageCode)
71
+
72
+ if appLanguageDirection == .leftToRight, systemLanguageDirection == .rightToLeft {
73
+ return true
74
+ }
75
+
76
+ if appLanguageDirection == .rightToLeft, systemLanguageDirection == .leftToRight {
77
+ return true
78
+ }
79
+
80
+ return false
81
+ }
82
+
83
+ private static func swizzelMethods() {
84
+ DispatchQueue.once {
85
+ UIViewController.swizzleMethodsForViewController()
86
+ Bundle.swizzleMethodsForBundle()
87
+ URLSession.swizzleMethodsForURLSession()
88
+ NumberFormatter.swizzleMethodsForNumberFormatter()
89
+ UIImageView.swizzleMethodsForImageView()
90
+ UILabel.swizzleMethodsForLabel()
91
+ UITextField.swizzleMethodsForTextField()
92
+ UITextView.swizzleMethodsForTextView()
93
+ UIWindow.swizzleMethodsForWindow()
94
+ UIView.swizzleMethodsForView()
95
+ }
96
+ }
97
+
98
+ private init() {
99
+ initializeManager()
100
+ }
101
+
102
+ public func initializeManager() {
103
+ guard !_hasInitialized else {
104
+ return
105
+ }
106
+ _hasInitialized = true
107
+
108
+ self._systemLanguage = Locale.preferredLanguages.first
109
+ let cachedAppLanguage = UserDefaults.standard.object(forKey: appLanguageStorageKey) as? String
110
+ if FWLanguageUtil.isValidLanguage(cachedAppLanguage) {
111
+ self.appLanguage = cachedAppLanguage
112
+ }
113
+ #if DEBUG
114
+ print("""
115
+ FWAppLanguageManager init method
116
+ current app language: \(self.appLanguage ?? "")
117
+ system language: \(self._systemLanguage ?? "")
118
+ """)
119
+ #endif
120
+ }
121
+
122
+ public func changeAppLanguage(_ language: String?) -> Bool {
123
+ guard let language = language, language.count > 0 else {
124
+ appLanguage = nil
125
+ UserDefaults.standard.removeObject(forKey: appLanguageStorageKey)
126
+ UserDefaults.standard.synchronize()
127
+ return true
128
+ }
129
+
130
+ if FWLanguageUtil.isValidLanguage(language) {
131
+ appLanguage = language
132
+ UserDefaults.standard.set(language, forKey: appLanguageStorageKey)
133
+ UserDefaults.standard.synchronize()
134
+ return true
135
+ } else {
136
+ return false
137
+ }
138
+ }
139
+ }