react-native-firework-sdk 1.3.0-beta.4 → 1.3.1-beta.2
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/android/build.gradle +2 -2
- package/android/src/main/AndroidManifest.xml +2 -2
- package/android/src/main/java/com/fireworksdk/bridge/{reactnative/FWInitializationProvider.kt → FWInitializationProvider.kt} +1 -1
- package/android/src/main/java/com/fireworksdk/bridge/models/FWVideoFeedConfigModel.kt +3 -1
- package/android/src/main/java/com/fireworksdk/bridge/models/FWVideoPlayerConfigModel.kt +1 -0
- package/android/src/main/java/com/fireworksdk/bridge/models/FWVideoShoppingProduct.kt +1 -0
- package/android/src/main/java/com/fireworksdk/bridge/reactnative/manager/FWVideoFeedManager.kt +5 -2
- package/android/src/main/java/com/fireworksdk/bridge/reactnative/module/FWNavigatorModule.kt +1 -1
- package/android/src/main/java/com/fireworksdk/bridge/reactnative/module/FWVideoShoppingModule.kt +69 -30
- package/android/src/main/java/com/fireworksdk/bridge/reactnative/module/FireworkSDKModule.kt +1 -1
- package/android/src/main/java/com/fireworksdk/bridge/reactnative/utils/FWEventUtils.kt +10 -5
- package/android/src/main/java/com/fireworksdk/bridge/utils/FWVideoPlayerUtils.kt +18 -0
- package/ios/Components/VideoFeed.swift +95 -35
- package/ios/Components/VideoFeedConfiguration.swift +15 -3
- package/ios/Components/VideoFeedManager.m +1 -0
- package/ios/Components/VideoPlayerConfiguration.swift +3 -2
- package/ios/Models/NativeToRN/FireworkEventName.swift +1 -0
- package/ios/Models/RNToNative/RCTConvert+FireworkSDKModule.swift +24 -4
- package/ios/Models/RNToNative/RCTConvert+VideoFeed.swift +1 -1
- package/ios/Modules/FireworkSDKModule/FireworkSDKModule+EventTracking.swift +5 -0
- package/ios/Modules/Shopping/CartViewController.swift +23 -40
- package/ios/Modules/Shopping/Product.swift +1 -0
- package/ios/Modules/Shopping/ProductInfoViewConfiguration.swift +1 -6
- package/ios/Modules/Shopping/ShoppingModule.m +6 -4
- package/ios/Modules/Shopping/ShoppingModule.swift +89 -31
- package/lib/commonjs/FireworkSDK.js +14 -3
- package/lib/commonjs/FireworkSDK.js.map +1 -1
- package/lib/commonjs/VideoShopping.js +77 -16
- package/lib/commonjs/VideoShopping.js.map +1 -1
- package/lib/commonjs/components/VideoFeed.js +28 -3
- package/lib/commonjs/components/VideoFeed.js.map +1 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/modules/ShoppingModule.js.map +1 -1
- package/lib/module/FireworkSDK.js +14 -3
- package/lib/module/FireworkSDK.js.map +1 -1
- package/lib/module/VideoShopping.js +70 -16
- package/lib/module/VideoShopping.js.map +1 -1
- package/lib/module/components/VideoFeed.js +28 -3
- package/lib/module/components/VideoFeed.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/modules/ShoppingModule.js.map +1 -1
- package/lib/typescript/FireworkSDK.d.ts +5 -0
- package/lib/typescript/VideoShopping.d.ts +15 -1
- package/lib/typescript/components/VideoFeed.d.ts +1 -0
- package/lib/typescript/index.d.ts +4 -4
- package/lib/typescript/models/ProductUnit.d.ts +12 -0
- package/lib/typescript/models/VideoFeedConfiguration.d.ts +33 -5
- package/lib/typescript/models/VideoPlayerConfiguration.d.ts +5 -1
- package/lib/typescript/modules/ShoppingModule.d.ts +2 -0
- package/package.json +2 -6
- package/react-native-firework-sdk.podspec +1 -1
- package/src/FireworkSDK.ts +15 -3
- package/src/VideoShopping.ts +94 -15
- package/src/components/VideoFeed.tsx +22 -1
- package/src/index.tsx +6 -1
- package/src/models/ProductUnit.ts +13 -0
- package/src/models/VideoFeedConfiguration.ts +34 -5
- package/src/models/VideoPlayerConfiguration.ts +5 -1
- package/src/modules/ShoppingModule.ts +2 -0
package/android/build.gradle
CHANGED
|
@@ -38,7 +38,7 @@ android {
|
|
|
38
38
|
defaultConfig {
|
|
39
39
|
minSdkVersion 21
|
|
40
40
|
targetSdkVersion getExtOrIntegerDefault('targetSdkVersion')
|
|
41
|
-
versionCode
|
|
41
|
+
versionCode 1
|
|
42
42
|
versionName "1.0.0"
|
|
43
43
|
}
|
|
44
44
|
|
|
@@ -168,7 +168,7 @@ def kotlin_version = getExtOrDefault('kotlinVersion')
|
|
|
168
168
|
dependencies {
|
|
169
169
|
|
|
170
170
|
// optional 1: firework sdk release verison
|
|
171
|
-
def firework_sdk_version = '
|
|
171
|
+
def firework_sdk_version = 'debug~landmark-SNAPSHOT'
|
|
172
172
|
implementation "com.github.loopsocial:firework_sdk:$firework_sdk_version"
|
|
173
173
|
|
|
174
174
|
// optional 2: firework sdk local version,
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
android:theme="@style/Theme.AppCompat.Light.NoActionBar"
|
|
13
13
|
/>
|
|
14
14
|
<provider
|
|
15
|
-
android:authorities="
|
|
16
|
-
android:name=".
|
|
15
|
+
android:authorities="${applicationId}.fwBridgeProvider"
|
|
16
|
+
android:name="com.fireworksdk.bridge.FWInitializationProvider"
|
|
17
17
|
android:exported="false"
|
|
18
18
|
/>
|
|
19
19
|
<meta-data
|
|
@@ -11,7 +11,9 @@ data class FWVideoFeedConfigModel(
|
|
|
11
11
|
var titlePosition: String? = null,
|
|
12
12
|
var playIcon: FWPlayIconModel? = null,
|
|
13
13
|
var showAdBadge: Boolean? = null,
|
|
14
|
-
var customLayoutName: String? = null
|
|
14
|
+
var customLayoutName: String? = null,
|
|
15
|
+
var enableAutoplay: Boolean? = null,
|
|
16
|
+
val gridColumns: Int? = null,
|
|
15
17
|
) : Parcelable {
|
|
16
18
|
|
|
17
19
|
@Parcelize
|
package/android/src/main/java/com/fireworksdk/bridge/reactnative/manager/FWVideoFeedManager.kt
CHANGED
|
@@ -78,6 +78,9 @@ class FWVideoFeedManager : SimpleViewManager<FWVideoFeed>() {
|
|
|
78
78
|
FWLogUtils.d { "VideoContentStatus.ContentLoaded" }
|
|
79
79
|
FWEventUtils.receiveVideoFeedLoadFinishedSuccessEvent(reactContext, videoFeedView.id)
|
|
80
80
|
}
|
|
81
|
+
else -> {
|
|
82
|
+
FWLogUtils.d { "VideoContentStatus: $status" }
|
|
83
|
+
}
|
|
81
84
|
}
|
|
82
85
|
}
|
|
83
86
|
})
|
|
@@ -86,13 +89,13 @@ class FWVideoFeedManager : SimpleViewManager<FWVideoFeed>() {
|
|
|
86
89
|
private fun addPlaylistGroupFeedListener(reactContext: ThemedReactContext, videoFeed: FWVideoFeed?) {
|
|
87
90
|
val playlistGroupFeedView = videoFeed?.playlistGroupFeedView
|
|
88
91
|
playlistGroupFeedView?.itemClickHandler = object : OnPlaylistGroupItemClickedListener {
|
|
89
|
-
override fun onItemClicked(index: Int, id: String
|
|
92
|
+
override fun onItemClicked(index: Int, title: String, id: String): Boolean {
|
|
90
93
|
val fwVideoFeedItemDetailsModel = FWVideoFeedItemDetailsModel(
|
|
91
94
|
index,
|
|
92
95
|
id,
|
|
93
96
|
0,
|
|
94
97
|
videoFeed?.videoFeedPropsModel?.source,
|
|
95
|
-
|
|
98
|
+
title,
|
|
96
99
|
videoFeed?.videoFeedPropsModel?.playlistGroup,
|
|
97
100
|
videoFeed?.videoFeedPropsModel?.playlist,
|
|
98
101
|
videoFeed?.videoFeedPropsModel?.channel,
|
package/android/src/main/java/com/fireworksdk/bridge/reactnative/module/FWNavigatorModule.kt
CHANGED
|
@@ -2,7 +2,7 @@ package com.fireworksdk.bridge.reactnative.module
|
|
|
2
2
|
|
|
3
3
|
import android.app.Activity
|
|
4
4
|
import com.facebook.react.bridge.*
|
|
5
|
-
import com.fireworksdk.bridge.
|
|
5
|
+
import com.fireworksdk.bridge.FWInitializationProvider
|
|
6
6
|
import com.fireworksdk.bridge.reactnative.models.FWNavigatorInterface
|
|
7
7
|
import com.fireworksdk.bridge.reactnative.utils.FWEventUtils
|
|
8
8
|
import com.fireworksdk.bridge.utils.FWLogUtils
|
package/android/src/main/java/com/fireworksdk/bridge/reactnative/module/FWVideoShoppingModule.kt
CHANGED
|
@@ -5,13 +5,15 @@ import com.facebook.react.bridge.*
|
|
|
5
5
|
import com.fireworksdk.bridge.models.FWVideoShoppingProduct
|
|
6
6
|
import com.fireworksdk.bridge.reactnative.models.FWVideoShoppingInterface
|
|
7
7
|
import com.fireworksdk.bridge.reactnative.utils.FWEventUtils
|
|
8
|
+
import com.fireworksdk.bridge.utils.FWDateUtils
|
|
8
9
|
import com.fireworksdk.bridge.utils.FWGsonUtil
|
|
9
10
|
import com.fireworksdk.bridge.utils.FWLogUtils
|
|
10
11
|
import com.google.gson.reflect.TypeToken
|
|
11
12
|
import com.loopnow.fireworklibrary.baya.Baya
|
|
12
13
|
import com.loopnow.fireworklibrary.baya.UpdateCartStatus
|
|
13
|
-
import com.loopnow.fireworklibrary.data.
|
|
14
|
-
import
|
|
14
|
+
import com.loopnow.fireworklibrary.data.ProductBuilder
|
|
15
|
+
import java.util.*
|
|
16
|
+
import kotlin.collections.HashMap
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
class FWVideoShoppingModule(
|
|
@@ -20,7 +22,7 @@ class FWVideoShoppingModule(
|
|
|
20
22
|
|
|
21
23
|
private var addToCartHandler: Triple<Int, String, String>? = null
|
|
22
24
|
private var cartClickHandler: Pair<Int, Activity>? = null
|
|
23
|
-
private val updateProductHandler: HashMap<String, List<
|
|
25
|
+
private val updateProductHandler: HashMap<String, List<ProductBuilder>> = HashMap()
|
|
24
26
|
|
|
25
27
|
@ReactMethod
|
|
26
28
|
override fun init() {
|
|
@@ -31,6 +33,14 @@ class FWVideoShoppingModule(
|
|
|
31
33
|
@ReactMethod
|
|
32
34
|
override fun updateVideoProducts(productArray: ReadableArray?, videoId: String?) {
|
|
33
35
|
FWLogUtils.d { "FWVideoShoppingModule updateVideoProduct: $videoId" }
|
|
36
|
+
|
|
37
|
+
FWEventUtils.sendLogMessageEvent(
|
|
38
|
+
reactApplicationContext,
|
|
39
|
+
"[Android] Send updateVideoProducts 1 video $videoId ${
|
|
40
|
+
FWDateUtils.getDateString(Date())
|
|
41
|
+
}"
|
|
42
|
+
)
|
|
43
|
+
|
|
34
44
|
if (videoId.isNullOrBlank()) {
|
|
35
45
|
return
|
|
36
46
|
}
|
|
@@ -45,37 +55,66 @@ class FWVideoShoppingModule(
|
|
|
45
55
|
}
|
|
46
56
|
|
|
47
57
|
val products = updateProductHandler[videoId] ?: return
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
58
|
+
val newProducts = products.map { product ->
|
|
59
|
+
val vps = videoShoppingProducts.filter { vp ->
|
|
60
|
+
vp.productId == product.externalId
|
|
61
|
+
}
|
|
62
|
+
if (vps.isNullOrEmpty()) {
|
|
63
|
+
product.copy()
|
|
64
|
+
} else {
|
|
65
|
+
val videoShoppingProduct = vps[0]
|
|
66
|
+
|
|
67
|
+
product.copy(
|
|
68
|
+
name = videoShoppingProduct.name ?: product.name,
|
|
69
|
+
description = videoShoppingProduct.description ?: product.description,
|
|
70
|
+
units = product.units?.map { productBuilderUnit ->
|
|
71
|
+
val vpUs = videoShoppingProduct.units?.filter { vpU ->
|
|
72
|
+
vpU.unitId == productBuilderUnit.externalId
|
|
73
|
+
}
|
|
74
|
+
if (vpUs.isNullOrEmpty()) {
|
|
75
|
+
productBuilderUnit.copy()
|
|
76
|
+
} else {
|
|
77
|
+
val videoShoppingProductUnit = vpUs[0]
|
|
78
|
+
|
|
79
|
+
productBuilderUnit.copy(
|
|
80
|
+
image = productBuilderUnit.image?.copy(
|
|
81
|
+
url = videoShoppingProductUnit.imageUrl ?: productBuilderUnit.image?.url,
|
|
82
|
+
),
|
|
83
|
+
url = videoShoppingProductUnit.url ?: productBuilderUnit.url,
|
|
84
|
+
name = videoShoppingProductUnit.name ?: productBuilderUnit.name,
|
|
85
|
+
price = productBuilderUnit.price?.copy(
|
|
86
|
+
amount = videoShoppingProductUnit.price?.amount?.toString() ?: productBuilderUnit.price?.amount,
|
|
87
|
+
currencyCode = videoShoppingProductUnit.price?.currencyCode ?: productBuilderUnit.price?.currencyCode,
|
|
88
|
+
),
|
|
89
|
+
options = videoShoppingProductUnit.options?.map {
|
|
90
|
+
ProductBuilder.ProductBuilderUnit.ProductBuilderUnitOption(
|
|
91
|
+
name = it.name,
|
|
92
|
+
value = it.value,
|
|
93
|
+
)
|
|
94
|
+
} ?: productBuilderUnit.options
|
|
95
|
+
)
|
|
71
96
|
}
|
|
72
97
|
}
|
|
73
|
-
|
|
74
|
-
}
|
|
98
|
+
)
|
|
75
99
|
}
|
|
76
100
|
}
|
|
101
|
+
|
|
102
|
+
FWEventUtils.sendLogMessageEvent(
|
|
103
|
+
reactApplicationContext,
|
|
104
|
+
"[Android] Send updateVideoProducts 2 video $videoId ${
|
|
105
|
+
FWDateUtils.getDateString(Date())
|
|
106
|
+
}"
|
|
107
|
+
)
|
|
108
|
+
|
|
77
109
|
UiThreadUtil.runOnUiThread {
|
|
78
|
-
Baya.updateProductsComplete(videoId)
|
|
110
|
+
Baya.updateProductsComplete(videoId, newProducts)
|
|
111
|
+
|
|
112
|
+
FWEventUtils.sendLogMessageEvent(
|
|
113
|
+
reactApplicationContext,
|
|
114
|
+
"[Android] Send updateVideoProducts 3 video $videoId ${
|
|
115
|
+
FWDateUtils.getDateString(Date())
|
|
116
|
+
}"
|
|
117
|
+
)
|
|
79
118
|
}
|
|
80
119
|
updateProductHandler.remove(videoId)
|
|
81
120
|
}
|
|
@@ -161,7 +200,7 @@ class FWVideoShoppingModule(
|
|
|
161
200
|
|
|
162
201
|
private fun productListener() {
|
|
163
202
|
Baya.productInterface = object: Baya.ProductInterface {
|
|
164
|
-
override fun hydrateProducts(videoId: String, products: List<
|
|
203
|
+
override fun hydrateProducts(videoId: String, products: List<ProductBuilder>) {
|
|
165
204
|
FWLogUtils.d { "FWVideoShoppingModule hydrateProducts, videoId: $videoId" }
|
|
166
205
|
if (videoId.isNullOrBlank() || products.isNullOrEmpty()) {
|
|
167
206
|
return
|
package/android/src/main/java/com/fireworksdk/bridge/reactnative/module/FireworkSDKModule.kt
CHANGED
|
@@ -11,7 +11,7 @@ import com.fireworksdk.bridge.utils.*
|
|
|
11
11
|
import org.json.JSONObject
|
|
12
12
|
import com.facebook.react.bridge.ReactMethod
|
|
13
13
|
import com.fireworksdk.bridge.models.FWAdBadgeConfigModel
|
|
14
|
-
import com.fireworksdk.bridge.
|
|
14
|
+
import com.fireworksdk.bridge.FWInitializationProvider
|
|
15
15
|
import com.fireworksdk.bridge.reactnative.models.FireworkSDKInterface
|
|
16
16
|
import com.fireworksdk.bridge.reactnative.pages.FWContainerActivity
|
|
17
17
|
import com.fireworksdk.bridge.reactnative.utils.FWEventUtils
|
|
@@ -5,12 +5,12 @@ import com.facebook.react.bridge.*
|
|
|
5
5
|
import com.facebook.react.modules.core.DeviceEventManagerModule
|
|
6
6
|
import com.facebook.react.uimanager.events.RCTEventEmitter
|
|
7
7
|
import com.fireworksdk.bridge.models.*
|
|
8
|
-
import com.fireworksdk.bridge.
|
|
8
|
+
import com.fireworksdk.bridge.FWInitializationProvider
|
|
9
9
|
import com.fireworksdk.bridge.reactnative.module.FireworkSDKModule
|
|
10
10
|
import com.fireworksdk.bridge.reactnative.pages.FWContainerActivity
|
|
11
11
|
import com.fireworksdk.bridge.utils.FWDateUtils
|
|
12
12
|
import com.fireworksdk.bridge.utils.FWLogUtils
|
|
13
|
-
import com.loopnow.fireworklibrary.data.
|
|
13
|
+
import com.loopnow.fireworklibrary.data.ProductBuilder
|
|
14
14
|
import java.util.*
|
|
15
15
|
|
|
16
16
|
object FWEventUtils {
|
|
@@ -19,7 +19,7 @@ object FWEventUtils {
|
|
|
19
19
|
FWLogUtils.d { "FWNavigatorModule pushNativeContainer: $props" }
|
|
20
20
|
val activity = FWInitializationProvider.INSTANCE.resumedActivity
|
|
21
21
|
|
|
22
|
-
if (activity == null) {
|
|
22
|
+
if (activity == null || FireworkSDKModule.appComponentName.isNullOrBlank()) {
|
|
23
23
|
promise?.resolve(false)
|
|
24
24
|
return
|
|
25
25
|
}
|
|
@@ -89,6 +89,8 @@ object FWEventUtils {
|
|
|
89
89
|
|
|
90
90
|
eventMap.putMap("info", contentMap)
|
|
91
91
|
|
|
92
|
+
sendLogMessageEvent(reactContext, "[Android] Send VideoFeedClick event ${info.id} ${FWDateUtils.getDateString(Date())}")
|
|
93
|
+
|
|
92
94
|
sendEvent(reactContext, FWEventName.VideoFeedClick.rawValue, eventMap)
|
|
93
95
|
}
|
|
94
96
|
|
|
@@ -147,18 +149,21 @@ object FWEventUtils {
|
|
|
147
149
|
sendEvent(reactContext, FWVideoShoppingEventName.ClickCartIcon.rawValue, eventMap)
|
|
148
150
|
}
|
|
149
151
|
|
|
150
|
-
fun sendUpdateProductsDetailsEvent(reactContext: ReactContext, products: List<
|
|
152
|
+
fun sendUpdateProductsDetailsEvent(reactContext: ReactContext, products: List<ProductBuilder>?, videoId: String?) {
|
|
151
153
|
val eventMap = Arguments.createMap()
|
|
152
154
|
|
|
153
155
|
val productArray = Arguments.createArray()
|
|
154
156
|
if (!products.isNullOrEmpty()) {
|
|
155
157
|
for (product in products) {
|
|
156
|
-
productArray.pushString(product.
|
|
158
|
+
productArray.pushString(product.externalId)
|
|
157
159
|
}
|
|
158
160
|
}
|
|
159
161
|
|
|
160
162
|
eventMap.putArray("productIds", productArray)
|
|
161
163
|
eventMap.putString("callbackId", videoId)
|
|
164
|
+
|
|
165
|
+
sendLogMessageEvent(reactContext, "[Android] Send UpdateProductDetails event video $videoId ${FWDateUtils.getDateString(Date())}")
|
|
166
|
+
|
|
162
167
|
sendEvent(reactContext, FWVideoShoppingEventName.UpdateProductDetails.rawValue, eventMap)
|
|
163
168
|
}
|
|
164
169
|
|
|
@@ -65,6 +65,14 @@ object FWVideoPlayerUtils {
|
|
|
65
65
|
}
|
|
66
66
|
feedView?.setTitlePosition(titlePosition)
|
|
67
67
|
VideoFeedProperties.displayAdLabel = config?.showAdBadge == true
|
|
68
|
+
feedView?.viewModel?.apply {
|
|
69
|
+
val columns = config?.gridColumns ?: 0
|
|
70
|
+
gridColumns = if (columns > 0) {
|
|
71
|
+
columns
|
|
72
|
+
} else {
|
|
73
|
+
2
|
|
74
|
+
}
|
|
75
|
+
}
|
|
68
76
|
}
|
|
69
77
|
|
|
70
78
|
fun setVideoFeedConfig(context: Context, feedView: VideoFeedView?, config: FWVideoFeedConfigModel?) {
|
|
@@ -87,10 +95,20 @@ object FWVideoPlayerUtils {
|
|
|
87
95
|
else -> FeedTitlePosition.ALIGN_BOTTOM
|
|
88
96
|
}
|
|
89
97
|
feedView?.setTitlePosition(titlePosition)
|
|
98
|
+
feedView?.setAutoPlayOnFeed(config?.enableAutoplay == true)
|
|
90
99
|
VideoFeedProperties.displayAdLabel = config?.showAdBadge == true
|
|
100
|
+
feedView?.viewModel?.apply {
|
|
101
|
+
val columns = config?.gridColumns ?: 0
|
|
102
|
+
gridColumns = if (columns > 0) {
|
|
103
|
+
columns
|
|
104
|
+
} else {
|
|
105
|
+
2
|
|
106
|
+
}
|
|
107
|
+
}
|
|
91
108
|
}
|
|
92
109
|
|
|
93
110
|
fun setVideoPlayerConfig(config: FWVideoPlayerConfigModel?) {
|
|
111
|
+
VideoPlayerProperties.branding = config?.showBranding != false
|
|
94
112
|
VideoPlayerProperties.share = config?.showShareButton != false
|
|
95
113
|
VideoPlayerProperties.loop = config?.videoCompleteAction != FWVideoPlayerConstant.FW_VIDEO_COMPLETE_ACTION_ADVANCE_TO_NEXT
|
|
96
114
|
// VideoPlayerProperties.autoPlayOnComplete = config?.videoCompleteAction != FWVideoPlayerConstant.FW_VIDEO_COMPLETE_ACTION_LOOP
|
|
@@ -11,22 +11,7 @@ import FireworkVideo
|
|
|
11
11
|
|
|
12
12
|
@objc
|
|
13
13
|
public enum VideoFeedMode: Int {
|
|
14
|
-
case row,
|
|
15
|
-
|
|
16
|
-
fileprivate var videoFeedLayout: VideoFeedLayout {
|
|
17
|
-
switch self {
|
|
18
|
-
case .row:
|
|
19
|
-
return VideoFeedHorizontalLayout()
|
|
20
|
-
case .colume:
|
|
21
|
-
let layout = VideoFeedGridLayout()
|
|
22
|
-
layout.numberOfColumns = 1
|
|
23
|
-
return layout
|
|
24
|
-
case .grid:
|
|
25
|
-
let layout = VideoFeedGridLayout()
|
|
26
|
-
layout.numberOfColumns = 2
|
|
27
|
-
return layout
|
|
28
|
-
}
|
|
29
|
-
}
|
|
14
|
+
case row, column, grid
|
|
30
15
|
}
|
|
31
16
|
|
|
32
17
|
@objc
|
|
@@ -55,7 +40,8 @@ public class VideoFeed: UIView, VideoFeedViewControllerDelegate {
|
|
|
55
40
|
guard let feedVC = feedVC else {
|
|
56
41
|
return
|
|
57
42
|
}
|
|
58
|
-
feedVC.viewConfiguration =
|
|
43
|
+
feedVC.viewConfiguration = convertToVideoFeedContentConfiguration()
|
|
44
|
+
feedVC.layout = videoFeedLayout
|
|
59
45
|
}
|
|
60
46
|
}
|
|
61
47
|
@objc public var playerViewConfig: VideoPlayerConfiguration? {
|
|
@@ -63,7 +49,7 @@ public class VideoFeed: UIView, VideoFeedViewControllerDelegate {
|
|
|
63
49
|
guard let feedVC = feedVC else {
|
|
64
50
|
return
|
|
65
51
|
}
|
|
66
|
-
feedVC.viewConfiguration =
|
|
52
|
+
feedVC.viewConfiguration = convertToVideoFeedContentConfiguration()
|
|
67
53
|
}
|
|
68
54
|
}
|
|
69
55
|
@objc var onVideoFeedLoadFinished: RCTBubblingEventBlock?
|
|
@@ -87,6 +73,60 @@ public class VideoFeed: UIView, VideoFeedViewControllerDelegate {
|
|
|
87
73
|
return .dynamicContent(channelID: channel, parameters: parameters ?? [:])
|
|
88
74
|
}
|
|
89
75
|
}
|
|
76
|
+
|
|
77
|
+
private var videoFeedLayout: VideoFeedLayout {
|
|
78
|
+
var resultLayout: VideoFeedLayout?
|
|
79
|
+
switch mode {
|
|
80
|
+
case .row:
|
|
81
|
+
let layout = VideoFeedHorizontalLayout()
|
|
82
|
+
resultLayout = layout
|
|
83
|
+
break
|
|
84
|
+
case .column:
|
|
85
|
+
let layout = VideoFeedGridLayout()
|
|
86
|
+
layout.numberOfColumns = 1
|
|
87
|
+
resultLayout = layout
|
|
88
|
+
break
|
|
89
|
+
case .grid:
|
|
90
|
+
let layout = VideoFeedGridLayout()
|
|
91
|
+
if let gridColumns = feedViewConfig?.gridColumns,
|
|
92
|
+
gridColumns > 0 {
|
|
93
|
+
layout.numberOfColumns = gridColumns
|
|
94
|
+
} else {
|
|
95
|
+
layout.numberOfColumns = 2
|
|
96
|
+
}
|
|
97
|
+
resultLayout = layout
|
|
98
|
+
break
|
|
99
|
+
}
|
|
100
|
+
if let config = feedViewConfig {
|
|
101
|
+
if let aspectRatio = config.aspectRatio {
|
|
102
|
+
if let horizontalLayout = resultLayout as? VideoFeedHorizontalLayout {
|
|
103
|
+
horizontalLayout.itemWidthRatio = aspectRatio
|
|
104
|
+
} else if let gridLayout = resultLayout as? VideoFeedGridLayout {
|
|
105
|
+
gridLayout.itemWidthRatio = aspectRatio
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if let contentPadding = config.contentPadding {
|
|
109
|
+
if let top = contentPadding.top {
|
|
110
|
+
resultLayout?.contentInsets.top = top
|
|
111
|
+
}
|
|
112
|
+
if let right = contentPadding.right {
|
|
113
|
+
resultLayout?.contentInsets.right = right
|
|
114
|
+
}
|
|
115
|
+
if let bottom = contentPadding.bottom {
|
|
116
|
+
resultLayout?.contentInsets.bottom = bottom
|
|
117
|
+
}
|
|
118
|
+
if let left = contentPadding.left {
|
|
119
|
+
resultLayout?.contentInsets.left = left
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if let itemSpacing = config.itemSpacing {
|
|
124
|
+
resultLayout?.itemSpacing = itemSpacing
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return resultLayout ?? VideoFeedHorizontalLayout()
|
|
129
|
+
}
|
|
90
130
|
|
|
91
131
|
public override func layoutSubviews() {
|
|
92
132
|
super.layoutSubviews()
|
|
@@ -103,11 +143,23 @@ public class VideoFeed: UIView, VideoFeedViewControllerDelegate {
|
|
|
103
143
|
}
|
|
104
144
|
|
|
105
145
|
let feedVC = VideoFeedViewController(
|
|
106
|
-
layout:
|
|
146
|
+
layout: videoFeedLayout,
|
|
107
147
|
source: source
|
|
108
148
|
)
|
|
109
|
-
|
|
110
|
-
|
|
149
|
+
|
|
150
|
+
var viewConfiguration = convertToVideoFeedContentConfiguration()
|
|
151
|
+
if viewConfiguration.itemView.autoplay.isEnabled {
|
|
152
|
+
viewConfiguration.itemView.autoplay.isEnabled = false
|
|
153
|
+
feedVC.viewConfiguration = viewConfiguration
|
|
154
|
+
DispatchQueue.main.async {
|
|
155
|
+
if feedVC.viewConfiguration == viewConfiguration {
|
|
156
|
+
viewConfiguration.itemView.autoplay.isEnabled = true
|
|
157
|
+
feedVC.viewConfiguration = viewConfiguration
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
} else {
|
|
161
|
+
feedVC.viewConfiguration = viewConfiguration
|
|
162
|
+
}
|
|
111
163
|
feedVC.delegate = self
|
|
112
164
|
self.feedVC = feedVC
|
|
113
165
|
|
|
@@ -157,14 +209,14 @@ public class VideoFeed: UIView, VideoFeedViewControllerDelegate {
|
|
|
157
209
|
}
|
|
158
210
|
|
|
159
211
|
extension VideoFeed {
|
|
160
|
-
private func
|
|
212
|
+
private func convertToVideoFeedContentConfiguration() -> VideoFeedContentConfiguration {
|
|
161
213
|
var videoConfig = VideoFeedContentConfiguration()
|
|
162
214
|
//set default value so that behavior will the same with Android
|
|
163
215
|
videoConfig.itemView.title.isHidden = false
|
|
164
216
|
videoConfig.itemView.titleLayoutConfiguration.titlePosition = .nested
|
|
165
217
|
|
|
166
|
-
let vfcConfig = VideoFeed.
|
|
167
|
-
let vpcConfig = VideoFeed.
|
|
218
|
+
let vfcConfig = VideoFeed.convertToVideoFeedItemContentConfiguration(feedViewConfig)
|
|
219
|
+
let vpcConfig = VideoFeed.convertToVideoPlayerContentConfiguration(playerViewConfig)
|
|
168
220
|
if let vfcConfig = vfcConfig {
|
|
169
221
|
videoConfig.itemView = vfcConfig
|
|
170
222
|
}
|
|
@@ -188,7 +240,7 @@ extension VideoFeed {
|
|
|
188
240
|
return videoConfig
|
|
189
241
|
}
|
|
190
242
|
|
|
191
|
-
private static func
|
|
243
|
+
private static func convertToVideoFeedItemContentConfiguration(_ config: VideoFeedConfiguration?) -> VideoFeedItemContentConfiguration? {
|
|
192
244
|
guard let config = config else {
|
|
193
245
|
return nil
|
|
194
246
|
}
|
|
@@ -199,7 +251,7 @@ extension VideoFeed {
|
|
|
199
251
|
vfcConfig.titleLayoutConfiguration.titlePosition = .nested
|
|
200
252
|
|
|
201
253
|
if let cornerRadius = config.cornerRadius {
|
|
202
|
-
vfcConfig.cornerRadius =
|
|
254
|
+
vfcConfig.cornerRadius = cornerRadius
|
|
203
255
|
}
|
|
204
256
|
if let title = config.title {
|
|
205
257
|
if let hidden = title.hidden {
|
|
@@ -209,7 +261,7 @@ extension VideoFeed {
|
|
|
209
261
|
vfcConfig.title.textColor = textcolor.uicolor()
|
|
210
262
|
}
|
|
211
263
|
if let fontSize = title.fontSize {
|
|
212
|
-
vfcConfig.title.font = UIFont.systemFont(ofSize:
|
|
264
|
+
vfcConfig.title.font = UIFont.systemFont(ofSize: fontSize)
|
|
213
265
|
}
|
|
214
266
|
}
|
|
215
267
|
if let playIcon = config.playIcon {
|
|
@@ -217,7 +269,7 @@ extension VideoFeed {
|
|
|
217
269
|
vfcConfig.playIcon.isHidden = hidden
|
|
218
270
|
}
|
|
219
271
|
if let iconWidth = playIcon.iconWidth {
|
|
220
|
-
vfcConfig.playIcon.iconWidth =
|
|
272
|
+
vfcConfig.playIcon.iconWidth = iconWidth
|
|
221
273
|
}
|
|
222
274
|
}
|
|
223
275
|
if let position = config.titlePosition {
|
|
@@ -234,11 +286,15 @@ extension VideoFeed {
|
|
|
234
286
|
if let showAdBadge = config.showAdBadge {
|
|
235
287
|
vfcConfig.sponsored.isHidden = !showAdBadge
|
|
236
288
|
}
|
|
289
|
+
|
|
290
|
+
if let enableAutoplay = config.enableAutoplay {
|
|
291
|
+
vfcConfig.autoplay.isEnabled = enableAutoplay
|
|
292
|
+
}
|
|
237
293
|
|
|
238
294
|
return vfcConfig
|
|
239
295
|
}
|
|
240
296
|
|
|
241
|
-
private static func
|
|
297
|
+
private static func convertToVideoPlayerContentConfiguration(_ config: VideoPlayerConfiguration?) -> VideoPlayerContentConfiguration? {
|
|
242
298
|
guard let config = config else {
|
|
243
299
|
return nil
|
|
244
300
|
}
|
|
@@ -265,13 +321,13 @@ extension VideoFeed {
|
|
|
265
321
|
}
|
|
266
322
|
if let ctaButtonStyle = config.ctaButtonStyle {
|
|
267
323
|
if let backgroundColor = ctaButtonStyle.backgroundColor {
|
|
268
|
-
vpcConfig.ctaButton.backgroundColor = backgroundColor.uicolor()
|
|
324
|
+
vpcConfig.ctaButton.contentConfiguration.backgroundColor = backgroundColor.uicolor()
|
|
269
325
|
}
|
|
270
326
|
if let textcolor = ctaButtonStyle.textColor {
|
|
271
|
-
vpcConfig.ctaButton.textColor = textcolor.uicolor()
|
|
327
|
+
vpcConfig.ctaButton.contentConfiguration.textColor = textcolor.uicolor()
|
|
272
328
|
}
|
|
273
329
|
if let fontSize = ctaButtonStyle.fontSize {
|
|
274
|
-
vpcConfig.ctaButton.font = UIFont.systemFont(ofSize:
|
|
330
|
+
vpcConfig.ctaButton.contentConfiguration.font = UIFont.systemFont(ofSize: fontSize)
|
|
275
331
|
}
|
|
276
332
|
}
|
|
277
333
|
if let showPlaybackButton = config.showPlaybackButton {
|
|
@@ -279,9 +335,13 @@ extension VideoFeed {
|
|
|
279
335
|
}
|
|
280
336
|
if let showMuteButton = config.showMuteButton {
|
|
281
337
|
vpcConfig.muteButton.isHidden = !showMuteButton
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
338
|
+
}
|
|
339
|
+
if let launchBehavior = config.launchBehavior {
|
|
340
|
+
vpcConfig.onFirstLaunch = launchBehavior.behavior()
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if let showBranding = config.showBranding {
|
|
344
|
+
vpcConfig.videoDetail.fireworkAttribution.isHidden = !showBranding
|
|
285
345
|
}
|
|
286
346
|
|
|
287
347
|
return vpcConfig
|
|
@@ -10,22 +10,34 @@ import Foundation
|
|
|
10
10
|
@objc
|
|
11
11
|
public class VideoFeedConfiguration: NSObject, Codable {
|
|
12
12
|
var backgroundColor: String?
|
|
13
|
-
var cornerRadius:
|
|
13
|
+
var cornerRadius: Double?
|
|
14
14
|
var title: VideoFeedTitleConfiguration?
|
|
15
15
|
var titlePosition: VideoFeedTitlePosition?
|
|
16
16
|
var playIcon: VideoFeedPlayIconConfiguration?
|
|
17
17
|
var showSponsored: Bool?
|
|
18
18
|
var showAdBadge: Bool?
|
|
19
|
+
var aspectRatio: Double?
|
|
20
|
+
var contentPadding: VideoFeedContentPadding?;
|
|
21
|
+
var itemSpacing: Double?
|
|
22
|
+
var enableAutoplay: Bool?
|
|
23
|
+
var gridColumns: Int?
|
|
19
24
|
|
|
20
25
|
class VideoFeedTitleConfiguration: NSObject, Codable {
|
|
21
26
|
public var hidden: Bool?
|
|
22
27
|
public var textColor: String?
|
|
23
|
-
public var fontSize:
|
|
28
|
+
public var fontSize: Double?
|
|
24
29
|
}
|
|
25
30
|
|
|
26
31
|
class VideoFeedPlayIconConfiguration: NSObject, Codable {
|
|
27
32
|
public var hidden: Bool?
|
|
28
|
-
public var iconWidth:
|
|
33
|
+
public var iconWidth: Double?
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
class VideoFeedContentPadding: NSObject, Codable {
|
|
37
|
+
public var top: Double?
|
|
38
|
+
public var right: Double?
|
|
39
|
+
public var bottom: Double?
|
|
40
|
+
public var left: Double?
|
|
29
41
|
}
|
|
30
42
|
|
|
31
43
|
enum VideoFeedTitlePosition: String, Codable {
|
|
@@ -16,7 +16,8 @@ public class VideoPlayerConfiguration: NSObject, Codable {
|
|
|
16
16
|
var showPlaybackButton: Bool?
|
|
17
17
|
var showMuteButton: Bool?
|
|
18
18
|
var launchBehavior: VideoLaunchBehavior?
|
|
19
|
-
|
|
19
|
+
var showBranding: Bool?
|
|
20
|
+
|
|
20
21
|
public enum VideoPlayerStyle: String, Codable {
|
|
21
22
|
case full, fit
|
|
22
23
|
}
|
|
@@ -28,7 +29,7 @@ public class VideoPlayerConfiguration: NSObject, Codable {
|
|
|
28
29
|
public class VideoPlayerCTAStyle: NSObject, Codable {
|
|
29
30
|
var backgroundColor: String?
|
|
30
31
|
var textColor: String?
|
|
31
|
-
var fontSize:
|
|
32
|
+
var fontSize: Double?
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
public enum VideoLaunchBehavior: String, Codable {
|
|
@@ -36,6 +36,7 @@ enum ShoppingEventName: String, CaseIterable {
|
|
|
36
36
|
case WillDisplayProduct = "fw:shopping:will-display-product"
|
|
37
37
|
case AddToCart = "fw:shopping:add-to-cart"
|
|
38
38
|
case ClickCartIcon = "fw:shopping:click-cart-icon"
|
|
39
|
+
case LogMessage = "fw:log-message"
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
/// Live stream event
|