react-native-firework-sdk 1.0.0-beta
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.
- package/README.md +39 -0
- package/android/build.gradle +193 -0
- package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/android/gradle/wrapper/gradle-wrapper.properties +5 -0
- package/android/gradle.properties +5 -0
- package/android/gradlew +185 -0
- package/android/gradlew.bat +89 -0
- package/android/publish.gradle +65 -0
- package/android/src/main/AndroidManifest.xml +16 -0
- package/android/src/main/java/com/reactnativefireworksdk/FireworkSDKPackage.kt +20 -0
- package/android/src/main/java/com/reactnativefireworksdk/components/videofeed/FWVideoFeed.kt +123 -0
- package/android/src/main/java/com/reactnativefireworksdk/constants/FWCommandConstant.kt +6 -0
- package/android/src/main/java/com/reactnativefireworksdk/constants/FWVideoPlayerConstant.kt +20 -0
- package/android/src/main/java/com/reactnativefireworksdk/manager/FWVideoFeedManager.kt +88 -0
- package/android/src/main/java/com/reactnativefireworksdk/models/FWEventName.kt +35 -0
- package/android/src/main/java/com/reactnativefireworksdk/models/FWVideoFeedConfigModel.kt +27 -0
- package/android/src/main/java/com/reactnativefireworksdk/models/FWVideoFeedItemDetailsModel.kt +11 -0
- package/android/src/main/java/com/reactnativefireworksdk/models/FWVideoFeedMode.kt +7 -0
- package/android/src/main/java/com/reactnativefireworksdk/models/FWVideoFeedSource.kt +7 -0
- package/android/src/main/java/com/reactnativefireworksdk/models/FWVideoFeedTitlePosition.kt +7 -0
- package/android/src/main/java/com/reactnativefireworksdk/models/FWVideoPlaybackDetails.kt +19 -0
- package/android/src/main/java/com/reactnativefireworksdk/models/FWVideoPlayerConfigModel.kt +20 -0
- package/android/src/main/java/com/reactnativefireworksdk/models/FWVideoShoppingInterface.kt +13 -0
- package/android/src/main/java/com/reactnativefireworksdk/models/FWVideoShoppingProduct.kt +34 -0
- package/android/src/main/java/com/reactnativefireworksdk/models/FireworkSDKInterface.kt +12 -0
- package/android/src/main/java/com/reactnativefireworksdk/module/FWVideoShoppingModule.kt +190 -0
- package/android/src/main/java/com/reactnativefireworksdk/module/FireworkSDKModule.kt +248 -0
- package/android/src/main/java/com/reactnativefireworksdk/pages/FWVideoShoppingCartActivity.kt +43 -0
- package/android/src/main/java/com/reactnativefireworksdk/utils/FWEventUtils.kt +127 -0
- package/android/src/main/java/com/reactnativefireworksdk/utils/FWJsonUtils.kt +57 -0
- package/android/src/main/java/com/reactnativefireworksdk/utils/FWLogUtils.kt +147 -0
- package/android/src/main/java/com/reactnativefireworksdk/utils/FWUrlUtils.kt +68 -0
- package/android/src/main/java/com/reactnativefireworksdk/utils/FWVideoPlayerUtils.kt +18 -0
- package/android/src/main/res/layout/fwrn_fragment_shoppingcart.xml +8 -0
- package/android/src/main/res/layout/fwrn_fragment_videofeed.xml +20 -0
- package/android/src/main/res/values/styles.xml +43 -0
- package/ios/Component/VideoFeed.swift +262 -0
- package/ios/Component/VideoFeedConfiguration.swift +32 -0
- package/ios/Component/VideoFeedManager.m +51 -0
- package/ios/Component/VideoFeedManager.swift +40 -0
- package/ios/Component/VideoPlayerConfiguration.swift +30 -0
- package/ios/FireworkSdk-Bridging-Header.h +6 -0
- package/ios/FireworkSdk.xcodeproj/project.pbxproj +291 -0
- package/ios/Models/FireworkJsEvent.swift +34 -0
- package/ios/Models/FireworkSDK+JsModel.swift +62 -0
- package/ios/Models/RCTConvert+FireworkSDKModule.swift +91 -0
- package/ios/Models/RCTConvert+Shopping.swift +49 -0
- package/ios/Models/RCTConvert+VideoFeed.swift +69 -0
- package/ios/Modules/FireworkSDKModule/FireworkSDKModule+CTA.swift +17 -0
- package/ios/Modules/FireworkSDKModule/FireworkSDKModule+EventTracking.swift +74 -0
- package/ios/Modules/FireworkSDKModule/FireworkSDKModule.m +19 -0
- package/ios/Modules/FireworkSDKModule/FireworkSDKModule.swift +106 -0
- package/ios/Modules/FireworkSDKModule/MobileADConfiguration.swift +17 -0
- package/ios/Modules/Shopping/CartViewController.swift +98 -0
- package/ios/Modules/Shopping/Product.swift +33 -0
- package/ios/Modules/Shopping/ProductInfoViewConfiguration.swift +24 -0
- package/ios/Modules/Shopping/ShoppingModule.m +19 -0
- package/ios/Modules/Shopping/ShoppingModule.swift +214 -0
- package/ios/Utils/String+Color.swift +38 -0
- package/ios/Utils/UIView+Constraints.swift +91 -0
- package/ios/Utils/UIView+ParentViewController.swift +21 -0
- package/ios/Utils/UIViewController+AttachChild.swift +69 -0
- package/lib/commonjs/FireworkSDK.js +171 -0
- package/lib/commonjs/FireworkSDK.js.map +1 -0
- package/lib/commonjs/VideoShopping.js +146 -0
- package/lib/commonjs/VideoShopping.js.map +1 -0
- package/lib/commonjs/components/CartContainer.js +35 -0
- package/lib/commonjs/components/CartContainer.js.map +1 -0
- package/lib/commonjs/components/FWVideoFeed.js +18 -0
- package/lib/commonjs/components/FWVideoFeed.js.map +1 -0
- package/lib/commonjs/components/VideoFeed.js +82 -0
- package/lib/commonjs/components/VideoFeed.js.map +1 -0
- package/lib/commonjs/constants/FWErrorMessage.js +15 -0
- package/lib/commonjs/constants/FWErrorMessage.js.map +1 -0
- package/lib/commonjs/index.js +44 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/models/ADConfig.js +2 -0
- package/lib/commonjs/models/ADConfig.js.map +1 -0
- package/lib/commonjs/models/AddToCartResult.js +2 -0
- package/lib/commonjs/models/AddToCartResult.js.map +1 -0
- package/lib/commonjs/models/FWError.js +2 -0
- package/lib/commonjs/models/FWError.js.map +1 -0
- package/lib/commonjs/models/FWEvents.js +20 -0
- package/lib/commonjs/models/FWEvents.js.map +1 -0
- package/lib/commonjs/models/FeedItemDetails.js +2 -0
- package/lib/commonjs/models/FeedItemDetails.js.map +1 -0
- package/lib/commonjs/models/Product.js +2 -0
- package/lib/commonjs/models/Product.js.map +1 -0
- package/lib/commonjs/models/ProductInfoViewConfiguration.js +2 -0
- package/lib/commonjs/models/ProductInfoViewConfiguration.js.map +1 -0
- package/lib/commonjs/models/ProductUnit.js +2 -0
- package/lib/commonjs/models/ProductUnit.js.map +1 -0
- package/lib/commonjs/models/VideoFeedConfiguration.js +2 -0
- package/lib/commonjs/models/VideoFeedConfiguration.js.map +1 -0
- package/lib/commonjs/models/VideoPlaybackDetails.js +2 -0
- package/lib/commonjs/models/VideoPlaybackDetails.js.map +1 -0
- package/lib/commonjs/models/VideoPlaybackEventName.js +23 -0
- package/lib/commonjs/models/VideoPlaybackEventName.js.map +1 -0
- package/lib/commonjs/models/VideoPlayerConfiguration.js +2 -0
- package/lib/commonjs/models/VideoPlayerConfiguration.js.map +1 -0
- package/lib/commonjs/modules/FireworkSDKModule.js +20 -0
- package/lib/commonjs/modules/FireworkSDKModule.js.map +1 -0
- package/lib/commonjs/modules/ShoppingModule.js +20 -0
- package/lib/commonjs/modules/ShoppingModule.js.map +1 -0
- package/lib/module/FireworkSDK.js +153 -0
- package/lib/module/FireworkSDK.js.map +1 -0
- package/lib/module/VideoShopping.js +134 -0
- package/lib/module/VideoShopping.js.map +1 -0
- package/lib/module/components/CartContainer.js +18 -0
- package/lib/module/components/CartContainer.js.map +1 -0
- package/lib/module/components/FWVideoFeed.js +8 -0
- package/lib/module/components/FWVideoFeed.js.map +1 -0
- package/lib/module/components/VideoFeed.js +66 -0
- package/lib/module/components/VideoFeed.js.map +1 -0
- package/lib/module/constants/FWErrorMessage.js +7 -0
- package/lib/module/constants/FWErrorMessage.js.map +1 -0
- package/lib/module/index.js +10 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/models/ADConfig.js +2 -0
- package/lib/module/models/ADConfig.js.map +1 -0
- package/lib/module/models/AddToCartResult.js +2 -0
- package/lib/module/models/AddToCartResult.js.map +1 -0
- package/lib/module/models/FWError.js +2 -0
- package/lib/module/models/FWError.js.map +1 -0
- package/lib/module/models/FWEvents.js +13 -0
- package/lib/module/models/FWEvents.js.map +1 -0
- package/lib/module/models/FeedItemDetails.js +2 -0
- package/lib/module/models/FeedItemDetails.js.map +1 -0
- package/lib/module/models/Product.js +2 -0
- package/lib/module/models/Product.js.map +1 -0
- package/lib/module/models/ProductInfoViewConfiguration.js +2 -0
- package/lib/module/models/ProductInfoViewConfiguration.js.map +1 -0
- package/lib/module/models/ProductUnit.js +2 -0
- package/lib/module/models/ProductUnit.js.map +1 -0
- package/lib/module/models/VideoFeedConfiguration.js +2 -0
- package/lib/module/models/VideoFeedConfiguration.js.map +1 -0
- package/lib/module/models/VideoPlaybackDetails.js +2 -0
- package/lib/module/models/VideoPlaybackDetails.js.map +1 -0
- package/lib/module/models/VideoPlaybackEventName.js +16 -0
- package/lib/module/models/VideoPlaybackEventName.js.map +1 -0
- package/lib/module/models/VideoPlayerConfiguration.js +2 -0
- package/lib/module/models/VideoPlayerConfiguration.js.map +1 -0
- package/lib/module/modules/FireworkSDKModule.js +10 -0
- package/lib/module/modules/FireworkSDKModule.js.map +1 -0
- package/lib/module/modules/ShoppingModule.js +10 -0
- package/lib/module/modules/ShoppingModule.js.map +1 -0
- package/lib/typescript/FireworkSDK.d.ts +74 -0
- package/lib/typescript/VideoShopping.d.ts +54 -0
- package/lib/typescript/components/CartContainer.d.ts +3 -0
- package/lib/typescript/components/FWVideoFeed.d.ts +2 -0
- package/lib/typescript/components/VideoFeed.d.ts +53 -0
- package/lib/typescript/constants/FWErrorMessage.d.ts +2 -0
- package/lib/typescript/index.d.ts +25 -0
- package/lib/typescript/models/ADConfig.d.ts +10 -0
- package/lib/typescript/models/AddToCartResult.d.ts +10 -0
- package/lib/typescript/models/FWError.d.ts +10 -0
- package/lib/typescript/models/FWEvents.d.ts +55 -0
- package/lib/typescript/models/FeedItemDetails.d.ts +14 -0
- package/lib/typescript/models/Product.d.ts +19 -0
- package/lib/typescript/models/ProductInfoViewConfiguration.d.ts +18 -0
- package/lib/typescript/models/ProductUnit.d.ts +22 -0
- package/lib/typescript/models/VideoFeedConfiguration.d.ts +32 -0
- package/lib/typescript/models/VideoPlaybackDetails.d.ts +31 -0
- package/lib/typescript/models/VideoPlaybackEventName.d.ts +39 -0
- package/lib/typescript/models/VideoPlayerConfiguration.d.ts +25 -0
- package/lib/typescript/modules/FireworkSDKModule.d.ts +12 -0
- package/lib/typescript/modules/ShoppingModule.d.ts +13 -0
- package/package.json +147 -0
- package/react-native-firework-sdk.podspec +22 -0
- package/src/FireworkSDK.ts +153 -0
- package/src/VideoShopping.ts +165 -0
- package/src/components/CartContainer.tsx +20 -0
- package/src/components/FWVideoFeed.tsx +10 -0
- package/src/components/VideoFeed.tsx +109 -0
- package/src/constants/FWErrorMessage.ts +11 -0
- package/src/index.tsx +109 -0
- package/src/models/ADConfig.ts +10 -0
- package/src/models/AddToCartResult.ts +10 -0
- package/src/models/FWError.ts +10 -0
- package/src/models/FWEvents.ts +63 -0
- package/src/models/FeedItemDetails.ts +14 -0
- package/src/models/Product.ts +20 -0
- package/src/models/ProductInfoViewConfiguration.ts +20 -0
- package/src/models/ProductUnit.ts +23 -0
- package/src/models/VideoFeedConfiguration.ts +35 -0
- package/src/models/VideoPlaybackDetails.ts +33 -0
- package/src/models/VideoPlaybackEventName.ts +40 -0
- package/src/models/VideoPlayerConfiguration.ts +26 -0
- package/src/modules/FireworkSDKModule.ts +27 -0
- package/src/modules/ShoppingModule.ts +29 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
//
|
|
2
|
+
// VideoFeedManager.m
|
|
3
|
+
// react-native-firework-sdk
|
|
4
|
+
//
|
|
5
|
+
// Created by Jeff Zheng on 2021/12/22.
|
|
6
|
+
//
|
|
7
|
+
// The FireworkSDKModule exposes instances of Objective-C (native) classes to JavaScript (JS) as JS objects
|
|
8
|
+
|
|
9
|
+
#import <React/RCTBridgeModule.h>
|
|
10
|
+
|
|
11
|
+
@interface RCT_EXTERN_REMAP_MODULE(FireworkSDK, FireworkSDKModule, NSObject)
|
|
12
|
+
|
|
13
|
+
_RCT_EXTERN_REMAP_METHOD(init, initializeSDK:(NSString * __nullable)userId config:(NSDictionary *__nullable)config, NO)
|
|
14
|
+
RCT_EXTERN_METHOD(openVideoPlayer:(NSString *)url config:(NSDictionary *)config)
|
|
15
|
+
RCT_EXTERN_METHOD(setCustomCTAClickEnabled:(BOOL)enabled)
|
|
16
|
+
RCT_EXTERN_METHOD(setShareBaseURL:(NSString *)url)
|
|
17
|
+
RCT_EXTERN_METHOD(setVideoPlaybackEventEnabled:(BOOL)enabled)
|
|
18
|
+
|
|
19
|
+
@end
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
//
|
|
2
|
+
// VideoFeedManager.m
|
|
3
|
+
// react-native-firework-sdk
|
|
4
|
+
//
|
|
5
|
+
// Created by Jeff Zheng on 2021/12/22.
|
|
6
|
+
//
|
|
7
|
+
// The FireworkSDKModule Wrap native FireworkVideoSDK class,then expose to JavaScript in Objective-C
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
import FireworkVideo
|
|
11
|
+
|
|
12
|
+
#if canImport(FireworkVideoGAMSupport)
|
|
13
|
+
import FireworkVideoGAMSupportSDK
|
|
14
|
+
#endif
|
|
15
|
+
#if canImport(FireworkVideoGIMASupport)
|
|
16
|
+
import FireworkVideoGIMASupport
|
|
17
|
+
#endif
|
|
18
|
+
|
|
19
|
+
var gShareBaseURL: String?
|
|
20
|
+
|
|
21
|
+
@objc(FireworkSDKModule)
|
|
22
|
+
class FireworkSDKModule: RCTEventEmitter, FireworkVideoSDKDelegate {
|
|
23
|
+
// var shareBaseURL: String?
|
|
24
|
+
var customCTAClickEnabled: Bool = false
|
|
25
|
+
|
|
26
|
+
/// Decide whether to send video playback event to RN.
|
|
27
|
+
var enableVideoPlayBackEvent: Bool = false
|
|
28
|
+
|
|
29
|
+
override func supportedEvents() -> [String]! {
|
|
30
|
+
FWEventName.allCases.map { $0.rawValue }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
override class func requiresMainQueueSetup() -> Bool {
|
|
34
|
+
return true
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@objc(initializeSDK:config:)
|
|
38
|
+
func initializeSDK(_ userId: String? = nil, config: [String: AnyObject]? = nil) {
|
|
39
|
+
DispatchQueue.main.async {
|
|
40
|
+
FireworkVideoSDK.initializeSDK(delegate: self, userID: userId)
|
|
41
|
+
FireworkVideoSDK.ctaDelegate = self
|
|
42
|
+
FireworkVideoSDK.eventTracking.videoPlaybackDelegate = self
|
|
43
|
+
FireworkVideoSDK.eventTracking.feedDelegate = self
|
|
44
|
+
|
|
45
|
+
if let config = RCTConvert.mobileADConfiguration(config) {
|
|
46
|
+
switch config.type {
|
|
47
|
+
case .admob:
|
|
48
|
+
#if canImport(FireworkVideoGAMSupport)
|
|
49
|
+
if let startMobileAds = config.startMobileAds {
|
|
50
|
+
FireworkVideoGAMSupportSDK.initializeSDK()
|
|
51
|
+
} else {
|
|
52
|
+
FireworkVideoGAMSupportSDK.initializeSDK(startGADMobileAds: false)
|
|
53
|
+
}
|
|
54
|
+
#endif
|
|
55
|
+
case .ima:
|
|
56
|
+
#if canImport(FireworkVideoGIMASupport)
|
|
57
|
+
FireworkVideoGIMASupportSDK.enableIMAAds()
|
|
58
|
+
#endif
|
|
59
|
+
default:
|
|
60
|
+
break
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@objc(openVideoPlayer:config:)
|
|
67
|
+
func openVideoPlayer(_ url: String, config: [String:AnyObject]) {
|
|
68
|
+
var finalConfig = RCTConvert.videoPlayerContentConfiguration(config)
|
|
69
|
+
if let baseUrl = gShareBaseURL {
|
|
70
|
+
finalConfig.shareButton.behavior.baseURL = URL(string: baseUrl)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
DispatchQueue.main.async {
|
|
74
|
+
guard let presentedVC = RCTPresentedViewController() else {
|
|
75
|
+
return
|
|
76
|
+
}
|
|
77
|
+
VideoFeedViewController.openVideoPlayer(with: url, finalConfig, presentedVC) { _ in
|
|
78
|
+
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
@objc(setCustomCTAClickEnabled:)
|
|
84
|
+
func setCustomCTAClickEnabled(_ enabled: Bool) {
|
|
85
|
+
customCTAClickEnabled = enabled
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
@objc(setShareBaseURL:)
|
|
89
|
+
func setShareBaseURL(_ url: String) {
|
|
90
|
+
gShareBaseURL = url
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
@objc(setVideoPlaybackEventEnabled:)
|
|
94
|
+
func setVideoPlaybackEventEnabled(_ enable: Bool) {
|
|
95
|
+
enableVideoPlayBackEvent = enable
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// MARK: - FireworkVideoSDKDelegate
|
|
99
|
+
func fireworkVideoDidLoadSuccessfully() {
|
|
100
|
+
sendEvent(withName: FWEventName.SDKInit.rawValue, body: [:])
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
func fireworkVideoDidLoadWith(error: FireworkVideoSDKError) {
|
|
104
|
+
sendEvent(withName: FWEventName.SDKInit.rawValue, body: ["error": [error.jsErrorName: error.recoverySuggestion ?? "default"]])
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//
|
|
2
|
+
// MobileADConfiguration.swift
|
|
3
|
+
// react-native-firework-sdk
|
|
4
|
+
//
|
|
5
|
+
// Created by Jeff Zheng on 2022/1/5.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import Foundation
|
|
9
|
+
|
|
10
|
+
struct MobileADConfiguration: Codable {
|
|
11
|
+
var type: ADType = .none
|
|
12
|
+
var startMobileAds: Bool? = true
|
|
13
|
+
|
|
14
|
+
enum ADType: String, Codable {
|
|
15
|
+
case none, admob, ima
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
//
|
|
2
|
+
// CartViewController.swift
|
|
3
|
+
// react-native-firework-sdk
|
|
4
|
+
//
|
|
5
|
+
// Created by Jeff Zheng on 2021/12/31.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import UIKit
|
|
9
|
+
import FireworkVideo
|
|
10
|
+
|
|
11
|
+
class CartViewController: UIViewController, CartViewRepresentable {
|
|
12
|
+
var indicator: UIActivityIndicatorView? = nil
|
|
13
|
+
var callbackId: Int!
|
|
14
|
+
var directShowCartPage = false
|
|
15
|
+
|
|
16
|
+
init(callbackId: Int) {
|
|
17
|
+
self.callbackId = callbackId
|
|
18
|
+
|
|
19
|
+
super.init(nibName: nil, bundle: nil)
|
|
20
|
+
|
|
21
|
+
NotificationCenter.default.addObserver(self, selector: #selector(showCustomCartView), name: Notification.Name(rawValue: "showCustomCartView"), object: nil)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
required init?(coder: NSCoder) {
|
|
25
|
+
super.init(coder: coder)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
override func viewWillAppear(_ animated: Bool) {
|
|
29
|
+
super.viewWillAppear(animated)
|
|
30
|
+
|
|
31
|
+
self.navigationController?.setNavigationBarHidden(true, animated: true)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
override func viewDidLoad() {
|
|
35
|
+
super.viewDidLoad()
|
|
36
|
+
|
|
37
|
+
if directShowCartPage {
|
|
38
|
+
directShowCustomCartView()
|
|
39
|
+
} else {
|
|
40
|
+
setupIndicatorView()
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
override func viewWillDisappear(_ animated: Bool) {
|
|
45
|
+
super.viewWillDisappear(animated)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
self.navigationController?.setNavigationBarHidden(false, animated: true)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@objc func showCustomCartView(_ noti: NSNotification) {
|
|
52
|
+
guard let cbId = noti.userInfo?["cbid"] as? Int, callbackId == cbId else {
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if isViewLoaded {
|
|
57
|
+
directShowCustomCartView()
|
|
58
|
+
} else {
|
|
59
|
+
directShowCartPage = true
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private func setupIndicatorView() {
|
|
64
|
+
let indicator = UIActivityIndicatorView(frame: .zero)
|
|
65
|
+
if #available(iOS 13.0, *) {
|
|
66
|
+
indicator.style = .large
|
|
67
|
+
} else {
|
|
68
|
+
indicator.style = .whiteLarge
|
|
69
|
+
}
|
|
70
|
+
indicator.color = UIColor.white
|
|
71
|
+
view.addSubview(indicator)
|
|
72
|
+
indicator.translatesAutoresizingMaskIntoConstraints = false
|
|
73
|
+
|
|
74
|
+
NSLayoutConstraint.activate([
|
|
75
|
+
indicator.centerXAnchor.constraint(equalTo: view.centerXAnchor),
|
|
76
|
+
indicator.centerYAnchor.constraint(equalTo: view.centerYAnchor)
|
|
77
|
+
])
|
|
78
|
+
|
|
79
|
+
indicator.startAnimating()
|
|
80
|
+
|
|
81
|
+
self.indicator = indicator
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
private func directShowCustomCartView() {
|
|
85
|
+
indicator?.stopAnimating()
|
|
86
|
+
|
|
87
|
+
let rctRootView = RCTRootView.init(bridge: RCTBridge.current(), moduleName: "FWShoppingCartPage", initialProperties: nil)
|
|
88
|
+
view.addSubview(rctRootView)
|
|
89
|
+
rctRootView.translatesAutoresizingMaskIntoConstraints = false
|
|
90
|
+
|
|
91
|
+
NSLayoutConstraint.activate([
|
|
92
|
+
rctRootView.leftAnchor.constraint(equalTo: view.leftAnchor),
|
|
93
|
+
rctRootView.topAnchor.constraint(equalTo: view.topAnchor),
|
|
94
|
+
rctRootView.rightAnchor.constraint(equalTo: view.rightAnchor),
|
|
95
|
+
rctRootView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
|
|
96
|
+
])
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Product.swift
|
|
3
|
+
// react-native-firework-sdk
|
|
4
|
+
//
|
|
5
|
+
// Created by Jeff Zheng on 2021/12/30.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import Foundation
|
|
9
|
+
|
|
10
|
+
struct Product: Codable {
|
|
11
|
+
var productId: String
|
|
12
|
+
var name: String?
|
|
13
|
+
var description: String?
|
|
14
|
+
var units: [ProductUnit]?
|
|
15
|
+
|
|
16
|
+
struct ProductUnit: Codable {
|
|
17
|
+
var unitId: String
|
|
18
|
+
var name: String?
|
|
19
|
+
var price: Price?
|
|
20
|
+
var url: String?
|
|
21
|
+
var options: [VariantOption]?
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
struct Price: Codable {
|
|
25
|
+
var amount: Double?
|
|
26
|
+
var currencyCode: String?
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
struct VariantOption: Codable, Equatable {
|
|
30
|
+
var name: String?
|
|
31
|
+
var value: String?
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//
|
|
2
|
+
// ProductInfoViewConfiguration.swift
|
|
3
|
+
// react-native-firework-sdk
|
|
4
|
+
//
|
|
5
|
+
// Created by Jeff Zheng on 2021/12/30.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import Foundation
|
|
9
|
+
|
|
10
|
+
struct ProductInfoViewConfiguration: Codable {
|
|
11
|
+
var cartIcon: CartIconConfiguration?
|
|
12
|
+
var addToCartButton: ButtonConfiguration?
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
struct CartIconConfiguration: Codable {
|
|
16
|
+
var isHidden: Bool? = false
|
|
17
|
+
}
|
|
18
|
+
struct ButtonConfiguration: Codable {
|
|
19
|
+
var backgroundColor: String?
|
|
20
|
+
var textColor: String?
|
|
21
|
+
var fontSize: Int?
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
//
|
|
2
|
+
// ShoppingModule.m
|
|
3
|
+
// react-native-firework-sdk
|
|
4
|
+
//
|
|
5
|
+
// Created by Jeff Zheng on 2021/12/30.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#import <React/RCTBridgeModule.h>
|
|
9
|
+
|
|
10
|
+
@interface RCT_EXTERN_REMAP_MODULE(ShoppingModule, ShoppingModule, NSObject)
|
|
11
|
+
|
|
12
|
+
_RCT_EXTERN_REMAP_METHOD(init, initialize, NO)
|
|
13
|
+
RCT_EXTERN_METHOD(updateVideoProduct:(NSDictionary *)product cbId:(nonnull NSNumber *)cbid)
|
|
14
|
+
RCT_EXTERN_METHOD(updateProductViewConfig:(NSDictionary *)config cbId:(nonnull NSNumber *)cbid)
|
|
15
|
+
RCT_EXTERN_METHOD(updateAddToCartStatus:(NSString *)res tips:(nullable NSString *)tips cbId:(nonnull NSNumber *)cbid)
|
|
16
|
+
RCT_EXTERN_METHOD(jumpToCartPage:(nonnull NSNumber *)cbid)
|
|
17
|
+
RCT_EXTERN_METHOD(exitCartPage)
|
|
18
|
+
|
|
19
|
+
@end
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
//
|
|
2
|
+
// File.swift
|
|
3
|
+
// react-native-firework-sdk
|
|
4
|
+
//
|
|
5
|
+
// Created by Jeff Zheng on 2021/12/30.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import Foundation
|
|
9
|
+
import FireworkVideo
|
|
10
|
+
|
|
11
|
+
@objc(ShoppingModule)
|
|
12
|
+
class ShoppingModule: RCTEventEmitter, FireworkVideoShoppingDelegate, CartViewControllerProviding {
|
|
13
|
+
|
|
14
|
+
private var productInfoViewConfigurator: (Int, ProductInfoViewConfigurable)?
|
|
15
|
+
private var addToCartHandler: (Int, AddToCartHandler)?
|
|
16
|
+
private var productHydrating: (Int, ProductHydrating)?
|
|
17
|
+
private var cartViewController: CartViewController?
|
|
18
|
+
|
|
19
|
+
override func supportedEvents() -> [String]! {
|
|
20
|
+
ShoppingEventName.allCases.map { $0.rawValue }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
override class func requiresMainQueueSetup() -> Bool {
|
|
24
|
+
return true
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
private static func generateCallbackId() -> Int {
|
|
28
|
+
struct CBIdIncreasingFactor {
|
|
29
|
+
static var callbackId = 0
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
CBIdIncreasingFactor.callbackId += 1;
|
|
33
|
+
return CBIdIncreasingFactor.callbackId
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@objc
|
|
37
|
+
func initialize() {
|
|
38
|
+
DispatchQueue.main.async {
|
|
39
|
+
FireworkVideoSDK.shopping.shoppingDelegate = self
|
|
40
|
+
FireworkVideoSDK.shopping.cartViewControllerProvider = self
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@objc
|
|
45
|
+
func updateVideoProduct(_ product: [String: Any]?, cbId: NSNumber) {
|
|
46
|
+
let product = RCTConvert.buildProduct(product)
|
|
47
|
+
guard let product = product, let productHydrating = productHydrating, productHydrating.0 == Int(truncating: cbId) else {
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
DispatchQueue.main.async {
|
|
52
|
+
productHydrating.1.hydrateProduct(product.productId, { build in
|
|
53
|
+
return ShoppingModule.hydrateProduct(product, build)
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@objc
|
|
59
|
+
func updateProductViewConfig(_ config: [String: Any]?, cbId: NSNumber) {
|
|
60
|
+
let config = RCTConvert.buildProductInfoViewConfiguration(config)
|
|
61
|
+
guard let config = config, let productInfoViewConfigurator = productInfoViewConfigurator, productInfoViewConfigurator.0 == Int(truncating: cbId) else {
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
DispatchQueue.main.async {
|
|
66
|
+
ShoppingModule.hydrateProductViewConfig(config, productInfoViewConfigurator.1)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@objc
|
|
71
|
+
func updateAddToCartStatus(_ res: String, tips: String?, cbId: NSNumber) {
|
|
72
|
+
guard let handler = addToCartHandler, handler.0 == Int(truncating: cbId) else {
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
DispatchQueue.main.async {
|
|
77
|
+
if res == "success" {
|
|
78
|
+
handler.1(.feedbackOnly(.success(message: tips ?? "success")))
|
|
79
|
+
} else {
|
|
80
|
+
handler.1(.feedbackOnly(.failure(message: tips ?? "failure")))
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@objc
|
|
86
|
+
func jumpToCartPage(_ cbId: NSNumber) {
|
|
87
|
+
DispatchQueue.main.async {
|
|
88
|
+
let noti = Notification(name: Notification.Name(rawValue: "showCustomCartView"), object: nil, userInfo: ["cbid": Int(truncating: cbId)])
|
|
89
|
+
NotificationCenter.default.post(noti)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
@objc
|
|
94
|
+
func exitCartPage() {
|
|
95
|
+
DispatchQueue.main.async {
|
|
96
|
+
self.cartViewController?.navigationController?.popViewController(animated: true)
|
|
97
|
+
self.cartViewController = nil
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// MARK: - FireworkVideoShoppingDelegate
|
|
102
|
+
func fireworkShopping(_ fireworkShopping: FireworkVideoShopping, willDisplayProductInfo productInfoViewConfigurator: ProductInfoViewConfigurable, forVideo video: VideoDetails) {
|
|
103
|
+
let callbackId = ShoppingModule.generateCallbackId()
|
|
104
|
+
self.productInfoViewConfigurator = (callbackId, productInfoViewConfigurator)
|
|
105
|
+
|
|
106
|
+
sendEvent(withName: ShoppingEventName.WillDisplayProduct.rawValue, body: ["videoId": video.videoID, "callbackId": callbackId])
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
func fireworkShopping(_ fireworkShopping: FireworkVideoShopping, updateDetailsForProducts products: [ProductID], forVideo video: VideoDetails, _ productHydrator: ProductHydrating) {
|
|
110
|
+
let callbackId = ShoppingModule.generateCallbackId()
|
|
111
|
+
self.productHydrating = (callbackId, productHydrator)
|
|
112
|
+
|
|
113
|
+
for productId in products {
|
|
114
|
+
sendEvent(withName: ShoppingEventName.UpdateProductDetails.rawValue, body: ["productId": productId, "callbackId": callbackId])
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
func fireworkShopping(_ fireworkShopping: FireworkVideoShopping, addProductVariantToCart item: SelectedProductVariant, fromVideo video: VideoDetails, _ addToCartCompletionHandler: @escaping AddToCartHandler) {
|
|
119
|
+
let callbackId = ShoppingModule.generateCallbackId()
|
|
120
|
+
self.addToCartHandler = (callbackId, addToCartCompletionHandler)
|
|
121
|
+
|
|
122
|
+
sendEvent(withName: ShoppingEventName.AddToCart.rawValue, body: ["productId": item.productID, "unitId": item.unitID, "callbackId": callbackId])
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// MARK: - CartViewControllerProviding
|
|
126
|
+
func cartViewController(for video: VideoDetails) -> FireworkVideo.CartViewController {
|
|
127
|
+
let callbackId = ShoppingModule.generateCallbackId()
|
|
128
|
+
sendEvent(withName: ShoppingEventName.ClickCartIcon.rawValue, body: ["callbackId": callbackId])
|
|
129
|
+
|
|
130
|
+
cartViewController = CartViewController(callbackId: callbackId)
|
|
131
|
+
return cartViewController!
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
private extension ShoppingModule {
|
|
136
|
+
|
|
137
|
+
static func hydrateProductViewConfig(_ config: ProductInfoViewConfiguration, _ productInfoViewConfigurator: ProductInfoViewConfigurable) {
|
|
138
|
+
if let cartIcon = config.cartIcon {
|
|
139
|
+
if let hidden = cartIcon.isHidden {
|
|
140
|
+
var config = productInfoViewConfigurator.shoppingCartIconConfiguration
|
|
141
|
+
config.isHidden = hidden
|
|
142
|
+
productInfoViewConfigurator.shoppingCartIconConfiguration = config
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if let buttonConfiguration = config.addToCartButton {
|
|
146
|
+
var config = productInfoViewConfigurator.productDetailsConfiguration
|
|
147
|
+
if let backgroundColor = buttonConfiguration.backgroundColor {
|
|
148
|
+
config.addToCartButton.backgroundColor = backgroundColor.uicolor()
|
|
149
|
+
}
|
|
150
|
+
if let textColor = buttonConfiguration.textColor {
|
|
151
|
+
config.addToCartButton.textColor = textColor.uicolor()
|
|
152
|
+
}
|
|
153
|
+
if let fontSize = buttonConfiguration.fontSize {
|
|
154
|
+
config.addToCartButton.font = UIFont.systemFont(ofSize: CGFloat(fontSize))
|
|
155
|
+
}
|
|
156
|
+
productInfoViewConfigurator.productDetailsConfiguration = config
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
private extension ShoppingModule {
|
|
163
|
+
|
|
164
|
+
static func hydrateProduct(_ product: Product, _ pbuild: ProductBuilder) -> ProductBuilder {
|
|
165
|
+
if let name = product.name {
|
|
166
|
+
pbuild.name(name)
|
|
167
|
+
}
|
|
168
|
+
if let description = product.description {
|
|
169
|
+
pbuild.description(description)
|
|
170
|
+
}
|
|
171
|
+
if let variants = product.units {
|
|
172
|
+
for variant in variants {
|
|
173
|
+
pbuild.variant(variant.unitId) { pvBuild in
|
|
174
|
+
return ShoppingModule.buildVariant(variant, pvBuild)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return pbuild
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
static func buildVariant(_ variant: Product.ProductUnit, _ pvBuild: ProductVariantBuilder) -> ProductVariantBuilder {
|
|
182
|
+
if let name = variant.name {
|
|
183
|
+
pvBuild.name(name)
|
|
184
|
+
}
|
|
185
|
+
if let url = variant.url {
|
|
186
|
+
pvBuild.url(url)
|
|
187
|
+
}
|
|
188
|
+
if let price = variant.price {
|
|
189
|
+
if let amount = price.amount, let currencyCode = price.currencyCode {
|
|
190
|
+
pvBuild.formattedPrice(Decimal(amount), currencyCode: currencyCode)
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
let options = ShoppingModule.buildVariantOption(variant.options)
|
|
194
|
+
if let options = options {
|
|
195
|
+
pvBuild.options(options)
|
|
196
|
+
}
|
|
197
|
+
return pvBuild
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
static func buildVariantOption(_ options: [Product.VariantOption]?) -> [String: String]? {
|
|
201
|
+
guard let options = options else {
|
|
202
|
+
return nil
|
|
203
|
+
}
|
|
204
|
+
var result: [String: String] = [:]
|
|
205
|
+
for option in options {
|
|
206
|
+
guard let name = option.name, let value = option.value else {
|
|
207
|
+
continue
|
|
208
|
+
}
|
|
209
|
+
result[name] = value
|
|
210
|
+
}
|
|
211
|
+
return result
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
//
|
|
2
|
+
// UIColor+Hexadecimal.swift
|
|
3
|
+
// react-native-firework-sdk
|
|
4
|
+
//
|
|
5
|
+
// Created by Jeff Zheng on 2021/12/22.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import UIKit
|
|
9
|
+
|
|
10
|
+
extension String {
|
|
11
|
+
|
|
12
|
+
func uicolor(alpha: CGFloat = 1.0) -> UIColor {
|
|
13
|
+
var red: UInt64 = 0, green: UInt64 = 0, blue: UInt64 = 0
|
|
14
|
+
var hex = self
|
|
15
|
+
//
|
|
16
|
+
if hex.hasPrefix("0x") || hex.hasPrefix("0X") {
|
|
17
|
+
hex = String(hex[hex.index(hex.startIndex, offsetBy: 2)...])
|
|
18
|
+
} else if hex.hasPrefix("#") {
|
|
19
|
+
hex = String(hex[hex.index(hex.startIndex, offsetBy: 1)...])
|
|
20
|
+
}
|
|
21
|
+
//
|
|
22
|
+
if hex.count < 6 {
|
|
23
|
+
for _ in 0..<6-hex.count {
|
|
24
|
+
hex += "0"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// convert
|
|
29
|
+
// red
|
|
30
|
+
Scanner(string: String(hex[..<hex.index(hex.startIndex, offsetBy: 2)])).scanHexInt64(&red)
|
|
31
|
+
// green
|
|
32
|
+
Scanner(string: String(hex[hex.index(hex.startIndex, offsetBy: 2)..<hex.index(hex.startIndex, offsetBy: 4)])).scanHexInt64(&green)
|
|
33
|
+
// blue
|
|
34
|
+
Scanner(string: String(hex[hex.index(startIndex, offsetBy: 4)...])).scanHexInt64(&blue)
|
|
35
|
+
|
|
36
|
+
return UIColor(red: CGFloat(red)/255.0, green: CGFloat(green)/255.0, blue: CGFloat(blue)/255.0, alpha: alpha)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
//
|
|
2
|
+
// View+CONTRAIT.swift
|
|
3
|
+
// react-native-firework-sdk
|
|
4
|
+
//
|
|
5
|
+
// Created by Jeff Zheng on 2021/12/30.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import Foundation
|
|
9
|
+
|
|
10
|
+
public struct LayoutDirection: OptionSet {
|
|
11
|
+
public let rawValue: Int
|
|
12
|
+
|
|
13
|
+
public init(rawValue: Int) {
|
|
14
|
+
self.rawValue = rawValue
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public static let top = LayoutDirection(rawValue: 1 << 0)
|
|
18
|
+
public static let bottom = LayoutDirection(rawValue: 1 << 1)
|
|
19
|
+
public static let leading = LayoutDirection(rawValue: 1 << 2)
|
|
20
|
+
public static let trailing = LayoutDirection(rawValue: 1 << 3)
|
|
21
|
+
|
|
22
|
+
public static let horizontal: LayoutDirection = [.leading, .trailing]
|
|
23
|
+
public static let vertical: LayoutDirection = [.top, .bottom]
|
|
24
|
+
|
|
25
|
+
public static let all: LayoutDirection = [.horizontal, .vertical]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public extension UILayoutPriority {
|
|
29
|
+
static var almostRequired: UILayoutPriority {
|
|
30
|
+
return .required - 1
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public extension NSLayoutConstraint {
|
|
35
|
+
func withPriority(_ new: UILayoutPriority) -> NSLayoutConstraint {
|
|
36
|
+
priority = new
|
|
37
|
+
return self
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public extension UIView {
|
|
42
|
+
func setContentHuggingPriorities(_ new: UILayoutPriority) {
|
|
43
|
+
setContentHuggingPriority(new, for: .horizontal)
|
|
44
|
+
setContentHuggingPriority(new, for: .vertical)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
func setContentCompressionResistancePriorities(_ new: UILayoutPriority) {
|
|
48
|
+
setContentCompressionResistancePriority(new, for: .horizontal)
|
|
49
|
+
setContentCompressionResistancePriority(new, for: .vertical)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
func constraints(equalTo other: UIView, directions: LayoutDirection = .all,
|
|
53
|
+
priority: UILayoutPriority = .required) -> [NSLayoutConstraint] {
|
|
54
|
+
var constraints: [NSLayoutConstraint] = []
|
|
55
|
+
if directions.contains(.top) {
|
|
56
|
+
constraints.append(topAnchor.constraint(equalTo: other.topAnchor).withPriority(priority))
|
|
57
|
+
}
|
|
58
|
+
if directions.contains(.leading) {
|
|
59
|
+
constraints.append(leadingAnchor.constraint(equalTo: other.leadingAnchor).withPriority(priority))
|
|
60
|
+
}
|
|
61
|
+
if directions.contains(.bottom) {
|
|
62
|
+
constraints.append(bottomAnchor.constraint(equalTo: other.bottomAnchor).withPriority(priority))
|
|
63
|
+
}
|
|
64
|
+
if directions.contains(.trailing) {
|
|
65
|
+
constraints.append(trailingAnchor.constraint(equalTo: other.trailingAnchor).withPriority(priority))
|
|
66
|
+
}
|
|
67
|
+
return constraints
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
func constraints(equalTo layoutGuide: UILayoutGuide, directions: LayoutDirection = .all,
|
|
71
|
+
priority: UILayoutPriority = .required) -> [NSLayoutConstraint] {
|
|
72
|
+
var constraints: [NSLayoutConstraint] = []
|
|
73
|
+
if directions.contains(.top) {
|
|
74
|
+
constraints.append(topAnchor.constraint(equalTo: layoutGuide.topAnchor).withPriority(priority))
|
|
75
|
+
}
|
|
76
|
+
if directions.contains(.leading) {
|
|
77
|
+
constraints.append(leadingAnchor.constraint(equalTo: layoutGuide.leadingAnchor).withPriority(priority))
|
|
78
|
+
}
|
|
79
|
+
if directions.contains(.bottom) {
|
|
80
|
+
constraints.append(bottomAnchor.constraint(equalTo: layoutGuide.bottomAnchor).withPriority(priority))
|
|
81
|
+
}
|
|
82
|
+
if directions.contains(.trailing) {
|
|
83
|
+
constraints.append(trailingAnchor.constraint(equalTo: layoutGuide.trailingAnchor).withPriority(priority))
|
|
84
|
+
}
|
|
85
|
+
return constraints
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
var isRightToLeft: Bool {
|
|
89
|
+
return UIView.userInterfaceLayoutDirection(for: semanticContentAttribute) == .rightToLeft
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//
|
|
2
|
+
// View+ParentViewController.swift
|
|
3
|
+
// react-native-firework-sdk
|
|
4
|
+
//
|
|
5
|
+
// Created by Jeff Zheng on 2021/12/22.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import UIKit
|
|
9
|
+
|
|
10
|
+
extension UIView {
|
|
11
|
+
var parentViewController: UIViewController? {
|
|
12
|
+
var parentResponder: UIResponder? = self
|
|
13
|
+
while parentResponder != nil {
|
|
14
|
+
parentResponder = parentResponder!.next
|
|
15
|
+
if let viewController = parentResponder as? UIViewController {
|
|
16
|
+
return viewController
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return nil
|
|
20
|
+
}
|
|
21
|
+
}
|