@shortkitsdk/react-native 0.2.34 → 0.2.36
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.kts +8 -0
- package/android/libs/shortkit-release.aar +0 -0
- package/android/src/main/java/com/shortkit/reactnative/ReactCarouselOverlayHost.kt +100 -47
- package/android/src/main/java/com/shortkit/reactnative/ReactOverlayHost.kt +54 -8
- package/android/src/main/java/com/shortkit/reactnative/ReactVideoCarouselOverlayHost.kt +240 -27
- package/android/src/main/java/com/shortkit/reactnative/ShortKitBridge.kt +151 -1
- package/android/src/main/java/com/shortkit/reactnative/ShortKitFeedView.kt +135 -6
- package/android/src/main/java/com/shortkit/reactnative/ShortKitFeedViewManager.kt +15 -0
- package/android/src/main/java/com/shortkit/reactnative/ShortKitModule.kt +21 -11
- package/android/src/main/java/com/shortkit/reactnative/ShortKitPackage.kt +0 -2
- package/android/src/test/java/com/shortkit/reactnative/ReactCarouselOverlayHostEmitTest.kt +134 -0
- package/android/src/test/java/com/shortkit/reactnative/ReactOverlayHostDragTest.kt +45 -0
- package/android/src/test/java/com/shortkit/reactnative/ReactVideoCarouselOverlayHostDragTest.kt +69 -0
- package/android/src/test/java/com/shortkit/reactnative/ReactVideoCarouselOverlayHostEmitTest.kt +144 -0
- package/android/src/test/java/com/shortkit/reactnative/ShortKitFeedViewActivePropTest.kt +57 -0
- package/ios/ReactOverlayHost.swift +10 -8
- package/ios/ReactVideoCarouselOverlayHost.swift +6 -5
- package/ios/ShortKitBridge.swift +18 -0
- package/ios/ShortKitModule.mm +5 -0
- package/ios/ShortKitPlayerNativeView.swift +36 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Info.plist +2 -2
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.abi.json +1252 -82
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +28 -2
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftinterface +28 -2
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/ShortKitSDK +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/_CodeSignature/CodeResources +9 -9
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Info.plist +2 -2
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +1252 -82
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +28 -2
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftinterface +28 -2
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.abi.json +1252 -82
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +28 -2
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +28 -2
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/ShortKitSDK +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/_CodeSignature/CodeResources +17 -17
- package/ios/ShortKitWidgetNativeView.swift +30 -3
- package/ios/ShortKitWidgetNativeViewManager.mm +1 -0
- package/package.json +1 -1
- package/src/ShortKitCommands.ts +20 -0
- package/src/ShortKitFeed.tsx +21 -0
- package/src/ShortKitPlayer.tsx +20 -1
- package/src/ShortKitWidget.tsx +63 -15
- package/src/specs/NativeShortKitModule.ts +10 -0
- package/src/specs/ShortKitWidgetViewNativeComponent.ts +15 -3
- package/src/types.ts +40 -0
- package/android/src/main/java/com/shortkit/reactnative/ShortKitPlayerNativeView.kt +0 -149
- package/android/src/main/java/com/shortkit/reactnative/ShortKitPlayerViewManager.kt +0 -35
- package/android/src/main/java/com/shortkit/reactnative/ShortKitWidgetNativeView.kt +0 -149
- package/android/src/main/java/com/shortkit/reactnative/ShortKitWidgetViewManager.kt +0 -30
package/android/build.gradle.kts
CHANGED
|
@@ -27,6 +27,10 @@ android {
|
|
|
27
27
|
java.srcDirs("src/main/java")
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
+
|
|
31
|
+
testOptions {
|
|
32
|
+
unitTests.isIncludeAndroidResources = true
|
|
33
|
+
}
|
|
30
34
|
}
|
|
31
35
|
|
|
32
36
|
dependencies {
|
|
@@ -47,4 +51,8 @@ dependencies {
|
|
|
47
51
|
implementation("com.squareup.okhttp3:okhttp:4.12.0")
|
|
48
52
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0")
|
|
49
53
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")
|
|
54
|
+
|
|
55
|
+
testImplementation("junit:junit:4.13.2")
|
|
56
|
+
testImplementation("org.robolectric:robolectric:4.14.1")
|
|
57
|
+
testImplementation("androidx.test:core:1.6.1")
|
|
50
58
|
}
|
|
Binary file
|
|
@@ -59,6 +59,52 @@ class ReactCarouselOverlayHost(context: Context) : FrameLayout(context), Carouse
|
|
|
59
59
|
/** Unique identifier for this overlay instance, used for event routing. */
|
|
60
60
|
val surfaceId: String = java.util.UUID.randomUUID().toString()
|
|
61
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Currently configured item id — used to detect item transitions so we
|
|
64
|
+
* can emit an event instead of triggering a full Fabric remount.
|
|
65
|
+
* Marked `internal` to allow unit-test verification.
|
|
66
|
+
*/
|
|
67
|
+
internal var currentItemId: String? = null
|
|
68
|
+
private set
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Whether initial props have been pushed at least once. First configure
|
|
72
|
+
* must go through pushProps()/setProperties; subsequent same-item
|
|
73
|
+
* rebinds are no-op; subsequent different-item rebinds emit
|
|
74
|
+
* onCarouselItemChanged for a React diff (no Fabric remount).
|
|
75
|
+
*/
|
|
76
|
+
internal var hasPushedInitialProps: Boolean = false
|
|
77
|
+
private set
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Test seam for emit-event interception. Production uses the default
|
|
81
|
+
* (forward to ShortKitBridge.shared); tests can overwrite to capture.
|
|
82
|
+
* Mirrors how iOS tests wrap bridge.emit().
|
|
83
|
+
*/
|
|
84
|
+
internal var emitEvent: (String, com.facebook.react.bridge.WritableMap) -> Unit =
|
|
85
|
+
{ name, params -> ShortKitBridge.shared?.emitEvent(name, params) }
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Test seam for WritableMap creation. Defaults to Arguments.createMap()
|
|
89
|
+
* (WritableNativeMap — requires JNI). Tests override with JavaOnlyMap()
|
|
90
|
+
* to avoid the native-library initialization requirement in Robolectric.
|
|
91
|
+
*/
|
|
92
|
+
internal var createMap: () -> com.facebook.react.bridge.WritableMap =
|
|
93
|
+
{ com.facebook.react.bridge.Arguments.createMap() }
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Test seam for ReactSurface creation. Production implementation resolves
|
|
97
|
+
* the surface from the ReactHost obtained from the application context.
|
|
98
|
+
* Tests override with a stub/mock so the surface is non-null after
|
|
99
|
+
* pushProps(), allowing hasPushedInitialProps to flip as expected.
|
|
100
|
+
* Returns null when surface creation fails (e.g. reactHost unavailable).
|
|
101
|
+
*/
|
|
102
|
+
internal var createSurface: (moduleName: String, initialProps: Bundle?) -> ReactSurface? =
|
|
103
|
+
{ moduleName, initialProps ->
|
|
104
|
+
(context.applicationContext as? ReactApplication)?.reactHost
|
|
105
|
+
?.createSurface(context, moduleName, initialProps)
|
|
106
|
+
}
|
|
107
|
+
|
|
62
108
|
private var cachedItemJSON: String? = null
|
|
63
109
|
private var isActive: Boolean = false
|
|
64
110
|
private var activeImageIndex: Int = 0
|
|
@@ -133,16 +179,12 @@ class ReactCarouselOverlayHost(context: Context) : FrameLayout(context), Carouse
|
|
|
133
179
|
isActive = false
|
|
134
180
|
activeImageIndex = 0
|
|
135
181
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
val gen = ++configureGeneration
|
|
182
|
+
val isSameItem = item.id == currentItemId
|
|
183
|
+
currentItemId = item.id
|
|
139
184
|
|
|
140
|
-
|
|
185
|
+
val gen = ++configureGeneration
|
|
141
186
|
pendingWriteJob?.cancel()
|
|
142
187
|
|
|
143
|
-
// Fast path: check for pre-existing temp files synchronously (just
|
|
144
|
-
// file.exists(), no I/O). This avoids the flicker of remote→local
|
|
145
|
-
// URL swap for images that were cached in a previous session.
|
|
146
188
|
val lookup = cachedImage
|
|
147
189
|
val fastItem = if (lookup != null) {
|
|
148
190
|
val fastImages = item.images.map { image ->
|
|
@@ -166,9 +208,23 @@ class ReactCarouselOverlayHost(context: Context) : FrameLayout(context), Carouse
|
|
|
166
208
|
|
|
167
209
|
val json = Json.encodeToString(fastItem)
|
|
168
210
|
cachedItemJSON = json
|
|
169
|
-
pushProps()
|
|
170
211
|
|
|
171
|
-
|
|
212
|
+
if (!hasPushedInitialProps) {
|
|
213
|
+
pushProps()
|
|
214
|
+
// Only flip the flag if the surface was actually created. If reactHost
|
|
215
|
+
// was null (createSurfaceIfNeeded silently returned), surface remains
|
|
216
|
+
// null and pendingProps is parked — stay in "first mount" mode so the
|
|
217
|
+
// next configure() retries pushProps() with the new item. Mirrors iOS
|
|
218
|
+
// ReactCarouselOverlayHost.swift:131-141.
|
|
219
|
+
if (surface != null) {
|
|
220
|
+
hasPushedInitialProps = true
|
|
221
|
+
}
|
|
222
|
+
} else if (!isSameItem) {
|
|
223
|
+
emitItemChanged(json)
|
|
224
|
+
}
|
|
225
|
+
// else: same item rebind — no emit, no Fabric prop push.
|
|
226
|
+
|
|
227
|
+
// Pre-size the surface view (existing logic, unchanged).
|
|
172
228
|
val parentView = parent as? android.view.View
|
|
173
229
|
val w = if (width > 0) width
|
|
174
230
|
else if (parentView != null && parentView.width > 0) parentView.width
|
|
@@ -178,54 +234,35 @@ class ReactCarouselOverlayHost(context: Context) : FrameLayout(context), Carouse
|
|
|
178
234
|
else context.resources.displayMetrics.heightPixels
|
|
179
235
|
measureAndLayoutSurfaceView(w, h)
|
|
180
236
|
|
|
181
|
-
// Background: write any newly-cached images to temp files
|
|
182
|
-
//
|
|
237
|
+
// Background: write any newly-cached images to temp files for next-time
|
|
238
|
+
// fast-path availability. iOS-aligned: write-only, no second pushProps()
|
|
239
|
+
// (next configure() picks up the local URL via the synchronous fast-path).
|
|
183
240
|
if (lookup == null) return
|
|
184
241
|
val hasRemoteUrls = fastItem.images.any { !it.url.startsWith("file://") }
|
|
185
242
|
if (!hasRemoteUrls) return
|
|
186
243
|
|
|
187
244
|
pendingWriteJob = ioScope.launch {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
val localUrl = writeTempImage(bitmap, image.url)
|
|
193
|
-
if (localUrl != null) CarouselImage(url = localUrl, alt = image.alt)
|
|
194
|
-
else image
|
|
195
|
-
} else {
|
|
196
|
-
image
|
|
197
|
-
}
|
|
245
|
+
withContext(Dispatchers.IO) {
|
|
246
|
+
item.images.forEach { image ->
|
|
247
|
+
if (gen != configureGeneration) return@forEach
|
|
248
|
+
lookup(image.url)?.let { writeTempImage(it, image.url) }
|
|
198
249
|
}
|
|
199
|
-
ImageCarouselItem(
|
|
200
|
-
id = item.id,
|
|
201
|
-
images = localImages,
|
|
202
|
-
caption = item.caption,
|
|
203
|
-
title = item.title,
|
|
204
|
-
description = item.description,
|
|
205
|
-
author = item.author,
|
|
206
|
-
section = item.section,
|
|
207
|
-
articleUrl = item.articleUrl,
|
|
208
|
-
)
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// Back on Main — only update if this configure() is still current
|
|
212
|
-
if (gen != configureGeneration) return@launch
|
|
213
|
-
val hasLocalImages = modifiedItem.images.any { it.url.startsWith("file://") }
|
|
214
|
-
if (hasLocalImages) {
|
|
215
|
-
val localJson = Json.encodeToString(modifiedItem)
|
|
216
|
-
cachedItemJSON = localJson
|
|
217
|
-
pushProps()
|
|
218
250
|
}
|
|
219
251
|
}
|
|
220
252
|
}
|
|
221
253
|
|
|
222
254
|
override fun activatePlayback() {
|
|
223
255
|
isActive = true
|
|
256
|
+
// Source mute state from the SDK rather than hardcoding true so
|
|
257
|
+
// overlay icons reflect actual mute state even on image carousels
|
|
258
|
+
// (which have no audio of their own but should still match host
|
|
259
|
+
// expectations).
|
|
260
|
+
val isMutedNow = ShortKitBridge.shared?.sdk?.player?.isMuted?.value ?: true
|
|
224
261
|
val params = com.facebook.react.bridge.Arguments.createMap().apply {
|
|
225
262
|
putString("surfaceId", surfaceId)
|
|
226
263
|
putBoolean("isActive", true)
|
|
227
264
|
putString("playerState", "idle")
|
|
228
|
-
putBoolean("isMuted",
|
|
265
|
+
putBoolean("isMuted", isMutedNow)
|
|
229
266
|
putDouble("playbackRate", 1.0)
|
|
230
267
|
putBoolean("captionsEnabled", false)
|
|
231
268
|
putNull("activeCue")
|
|
@@ -252,6 +289,21 @@ class ReactCarouselOverlayHost(context: Context) : FrameLayout(context), Carouse
|
|
|
252
289
|
applySurfaceProps(bundle)
|
|
253
290
|
}
|
|
254
291
|
|
|
292
|
+
/**
|
|
293
|
+
* Emit onCarouselItemChanged for a React diff update on cell reuse.
|
|
294
|
+
* Replaces pushProps() on subsequent item changes to avoid Fabric remount.
|
|
295
|
+
* Payload shape mirrors iOS ReactCarouselOverlayHost.swift:151-158.
|
|
296
|
+
*/
|
|
297
|
+
private fun emitItemChanged(json: String) {
|
|
298
|
+
val params = createMap().apply {
|
|
299
|
+
putString("surfaceId", surfaceId)
|
|
300
|
+
putString("item", json)
|
|
301
|
+
putBoolean("isActive", false)
|
|
302
|
+
putInt("activeImageIndex", 0)
|
|
303
|
+
}
|
|
304
|
+
emitEvent("onCarouselItemChanged", params)
|
|
305
|
+
}
|
|
306
|
+
|
|
255
307
|
/** Apply props to the surface, handling all lifecycle states. */
|
|
256
308
|
private fun applySurfaceProps(bundle: Bundle) {
|
|
257
309
|
val s = surface
|
|
@@ -369,11 +421,6 @@ class ReactCarouselOverlayHost(context: Context) : FrameLayout(context), Carouse
|
|
|
369
421
|
private fun createSurfaceIfNeeded() {
|
|
370
422
|
if (surface != null) return
|
|
371
423
|
|
|
372
|
-
val reactHost = (context.applicationContext as? ReactApplication)?.reactHost
|
|
373
|
-
if (reactHost == null) {
|
|
374
|
-
android.util.Log.e(TAG, "createSurface FAILED: reactHost is null")
|
|
375
|
-
return
|
|
376
|
-
}
|
|
377
424
|
val moduleName = "ShortKitCarouselOverlay_$carouselOverlayName"
|
|
378
425
|
|
|
379
426
|
// Pass pending props as initial props so the JS component has data on first render.
|
|
@@ -382,7 +429,13 @@ class ReactCarouselOverlayHost(context: Context) : FrameLayout(context), Carouse
|
|
|
382
429
|
val initialProps = pendingProps
|
|
383
430
|
pendingProps = null
|
|
384
431
|
|
|
385
|
-
val newSurface =
|
|
432
|
+
val newSurface = createSurface(moduleName, initialProps)
|
|
433
|
+
if (newSurface == null) {
|
|
434
|
+
android.util.Log.e(TAG, "createSurface FAILED: reactHost is null")
|
|
435
|
+
// Restore pending props so a future createSurfaceIfNeeded() can retry.
|
|
436
|
+
if (initialProps != null) pendingProps = initialProps
|
|
437
|
+
return
|
|
438
|
+
}
|
|
386
439
|
surface = newSurface
|
|
387
440
|
|
|
388
441
|
newSurface.view?.let { surfaceView ->
|
|
@@ -99,6 +99,21 @@ class ReactOverlayHost(context: Context) : FrameLayout(context), FeedOverlay {
|
|
|
99
99
|
private var cachedActiveCue: JSONObject? = null
|
|
100
100
|
private var cachedFeedScrollPhase: String? = null
|
|
101
101
|
|
|
102
|
+
/**
|
|
103
|
+
* Tracks active drag phase. While true, per-frame player-state emissions
|
|
104
|
+
* (playerState, isMuted, playbackRate, captionsEnabled, activeCue, time)
|
|
105
|
+
* are suppressed; emitFullState() re-syncs cached values on settle.
|
|
106
|
+
*
|
|
107
|
+
* Mirrors iOS ReactOverlayHost.swift:51 isDragging field.
|
|
108
|
+
*/
|
|
109
|
+
internal var isDragging: Boolean = false
|
|
110
|
+
private set
|
|
111
|
+
|
|
112
|
+
/** Test-only setter for [isDragging]. */
|
|
113
|
+
internal var isDraggingForTest: Boolean
|
|
114
|
+
get() = isDragging
|
|
115
|
+
set(value) { isDragging = value }
|
|
116
|
+
|
|
102
117
|
// Time coalescing (250ms)
|
|
103
118
|
private var cachedCurrentTime: Double = 0.0
|
|
104
119
|
private var cachedDuration: Double = 0.0
|
|
@@ -172,15 +187,29 @@ class ReactOverlayHost(context: Context) : FrameLayout(context), FeedOverlay {
|
|
|
172
187
|
cachedDuration = 0.0
|
|
173
188
|
cachedBuffered = 0.0
|
|
174
189
|
cachedPlayerState = "idle"
|
|
175
|
-
|
|
190
|
+
// Read the SDK's actual current mute state instead of hardcoding
|
|
191
|
+
// `true`. The flow subscription below will keep this in sync going
|
|
192
|
+
// forward, but during the brief window between configure() and the
|
|
193
|
+
// first emission the overlay's initialProps would otherwise carry a
|
|
194
|
+
// stale `true`, causing the next-cell mute toggle to compute the
|
|
195
|
+
// wrong target value (the user's tap on the new cell would resolve
|
|
196
|
+
// to setMuted(false) regardless of actual state).
|
|
197
|
+
player?.isMuted?.value?.let { cachedIsMuted = it }
|
|
176
198
|
cachedPlaybackRate = 1.0
|
|
177
199
|
cachedCaptionsEnabled = false
|
|
178
200
|
cachedActiveCue = null
|
|
179
201
|
cachedFeedScrollPhase = null
|
|
202
|
+
isDragging = false
|
|
180
203
|
|
|
181
204
|
if (surface == null) {
|
|
182
205
|
createSurfaceIfNeeded()
|
|
183
|
-
|
|
206
|
+
// Only flip the flag if surface was actually created. If reactHost was
|
|
207
|
+
// null (createSurfaceIfNeeded silently returned), surface remains null
|
|
208
|
+
// and the next configure() will retry. Mirrors iOS pattern and matches
|
|
209
|
+
// the carousel host fix from 6e5e1dce.
|
|
210
|
+
if (surface != null) {
|
|
211
|
+
surfaceHasStarted = true
|
|
212
|
+
}
|
|
184
213
|
} else if (!surfaceHasStarted) {
|
|
185
214
|
(surface as? ReactSurfaceImpl)?.updateInitProps(buildInitialPropsBundle())
|
|
186
215
|
surfaceHasStarted = true
|
|
@@ -403,7 +432,7 @@ class ReactOverlayHost(context: Context) : FrameLayout(context), FeedOverlay {
|
|
|
403
432
|
scope.launch {
|
|
404
433
|
player.playerState.collect { state ->
|
|
405
434
|
cachedPlayerState = ShortKitBridge.playerStateString(state)
|
|
406
|
-
if (isActive) {
|
|
435
|
+
if (isActive && !isDragging) {
|
|
407
436
|
val params = Arguments.createMap().apply {
|
|
408
437
|
putString("surfaceId", surfaceId)
|
|
409
438
|
putString("playerState", cachedPlayerState)
|
|
@@ -417,7 +446,7 @@ class ReactOverlayHost(context: Context) : FrameLayout(context), FeedOverlay {
|
|
|
417
446
|
scope.launch {
|
|
418
447
|
player.isMuted.collect { muted ->
|
|
419
448
|
cachedIsMuted = muted
|
|
420
|
-
if (isActive) {
|
|
449
|
+
if (isActive && !isDragging) {
|
|
421
450
|
val params = Arguments.createMap().apply {
|
|
422
451
|
putString("surfaceId", surfaceId)
|
|
423
452
|
putBoolean("isMuted", cachedIsMuted)
|
|
@@ -431,7 +460,7 @@ class ReactOverlayHost(context: Context) : FrameLayout(context), FeedOverlay {
|
|
|
431
460
|
scope.launch {
|
|
432
461
|
player.playbackRate.collect { rate ->
|
|
433
462
|
cachedPlaybackRate = rate.toDouble()
|
|
434
|
-
if (isActive) {
|
|
463
|
+
if (isActive && !isDragging) {
|
|
435
464
|
val params = Arguments.createMap().apply {
|
|
436
465
|
putString("surfaceId", surfaceId)
|
|
437
466
|
putDouble("playbackRate", cachedPlaybackRate)
|
|
@@ -445,7 +474,7 @@ class ReactOverlayHost(context: Context) : FrameLayout(context), FeedOverlay {
|
|
|
445
474
|
scope.launch {
|
|
446
475
|
player.captionsEnabled.collect { enabled ->
|
|
447
476
|
cachedCaptionsEnabled = enabled
|
|
448
|
-
if (isActive) {
|
|
477
|
+
if (isActive && !isDragging) {
|
|
449
478
|
val params = Arguments.createMap().apply {
|
|
450
479
|
putString("surfaceId", surfaceId)
|
|
451
480
|
putBoolean("captionsEnabled", cachedCaptionsEnabled)
|
|
@@ -467,7 +496,7 @@ class ReactOverlayHost(context: Context) : FrameLayout(context), FeedOverlay {
|
|
|
467
496
|
} else {
|
|
468
497
|
null
|
|
469
498
|
}
|
|
470
|
-
if (isActive) {
|
|
499
|
+
if (isActive && !isDragging) {
|
|
471
500
|
val params = Arguments.createMap().apply {
|
|
472
501
|
putString("surfaceId", surfaceId)
|
|
473
502
|
val cueJson = cachedActiveCue?.toString()
|
|
@@ -488,7 +517,7 @@ class ReactOverlayHost(context: Context) : FrameLayout(context), FeedOverlay {
|
|
|
488
517
|
cachedCurrentTime = time.currentMs / 1000.0
|
|
489
518
|
cachedDuration = time.durationMs / 1000.0
|
|
490
519
|
cachedBuffered = time.bufferedMs / 1000.0
|
|
491
|
-
if (isActive) {
|
|
520
|
+
if (isActive && !isDragging) {
|
|
492
521
|
timeDirty = true
|
|
493
522
|
}
|
|
494
523
|
}
|
|
@@ -504,6 +533,23 @@ class ReactOverlayHost(context: Context) : FrameLayout(context), FeedOverlay {
|
|
|
504
533
|
}.toString()
|
|
505
534
|
is FeedScrollPhase.Settled -> """{"phase":"settled"}"""
|
|
506
535
|
}
|
|
536
|
+
when (phase) {
|
|
537
|
+
is FeedScrollPhase.Dragging -> {
|
|
538
|
+
isDragging = true
|
|
539
|
+
stopTimeCoalescing()
|
|
540
|
+
}
|
|
541
|
+
is FeedScrollPhase.Settled -> {
|
|
542
|
+
val wasDragging = isDragging
|
|
543
|
+
isDragging = false
|
|
544
|
+
if (isActive) {
|
|
545
|
+
startTimeCoalescing()
|
|
546
|
+
if (wasDragging) {
|
|
547
|
+
emitFullState()
|
|
548
|
+
return@collect // emitFullState includes feedScrollPhase
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
}
|
|
507
553
|
if (isActive) {
|
|
508
554
|
val params = Arguments.createMap().apply {
|
|
509
555
|
putString("surfaceId", surfaceId)
|